Mentawai Web Framework

Filtros

Um filtro intercepta uma action para modificar sua entrada (input) e saída (output), antes e depois da action ser executada. Filtros são a principal funcionalidade do framework a partir da qual todas as outras (autenticação, validação, ioc, etc.) são implementadas. O Mentawai já vem com diversos filtros prontos para serem usados no pacote org.mentawai.filter. Entretanto é importante entender como eles funcionam para que seja possível implementar os seus próprios filtros caso necessário.

No Mentawai você define filtros na classe ApplicationManager.java do mesmo jeito que você define uma ActionConfig. Para entender filtros, vamos codificar um filtro genérico que tentará popular um objeto (para quem está vindo de Struts um FormBean) com os parâmetros de entrada da action. Você pode clicar aqui para baixar esse projeto se quiser. Note que o Mentawai já vem com um filtro completo e pronto para essa função chamado org.mentawai.filter.VOFilter. Para mais detalhes sobre esse filtro, consulte a seção sobre VO/OV/Inj/Out Filters.


A interface do filtro

A interface do filtro tem dois métodos:

package org.mentawai.core;

public interface Filter {
	
    public String filter(InvocationChain chain) throws Exception;
    
    public void destroy();
	
}					
					

Uma action pode ter mais de um filtro, logo para cada action um InvocationChain é criado. A classe InvocationChain tem dois métodos importantes:

  • getAction(): que retorna a action dessa invocation chain (cadeia de chamadas)


  • invoke(): que executa o próximo passo na cadeia de chamadas, em outras palavras, o próximo filtro ou a action se não houver mais filtros. A action é o último passo executado numa InvocationChain.


É importante entender que o filtro pode alterar a action antes e depois dela ser executada. Verifique o código abaixo para um exemplo:

import org.mentawai.core.*;

public class MyFilter implements Filter {
	
    public String filter(InvocationChain chain) throws Exception {
        Action action = chain.getAction();
        Output output = action.getOutput();
        output.setValue("before", "doing something before!");
        String result = chain.invoke();
        output.setValue("after", "doing something after!");
        return result;
    }
    
    public void destroy() { }
}
		

Vamos codificar um filtro útil de value object genérico para você entender como é fácil trabalhar com filtros no mentawai.


A interface AfterConsequenceFilter

É possível também criar um filtro para realizar uma tarefa após a execução da consequência da action. Por exemplo, o filtro org.mentawai.filter.ConnectionFilter retorna a conexão para o pool apenas após que a consequência da action ter sido executada, de forma que a conexão fique disponível enquanto o JSP é processado.

A interface org.mentawai.core.AfterConsequenceFilter é listada abaixo:

    package org.mentawai.core;
    
    public interface AfterConsequenceFilter extends Filter {
        
        public void afterConsequence(Action action, Consequence c, boolean conseqExecuted);
        
    }
    

O método afterConsequence será chamado pelo filtro após a execução da consequencia da action. Repare que esse método será executado mesmo que haja um exception e a consequência não seja totalmente executada.


O filtro do Value Object

O código abaixo pega todos os valores que estão no input da action e tenta injetar num objeto pré-definido no construtor do filtro. Após essa operação o objeto será adicionado ao input da action com uma chave também passada no construtor do filtro. Caso essa chave não tenha sido passada será usada o nome da classe como chave.

package org.mentawai.filter;

import java.lang.reflect.*;
import java.util.*;

import org.mentawai.core.*;

public class VOFilter implements Filter {
	
    private Class klass;
    private String key = null;
    private Map cache = new HashMap();
    private boolean tryField = false; // tries to access private fields...
	
    public VOFilter(Class klass) {
        this.klass = klass;
    }
    
    public VOFilter(Class klass, String key) {
        this(klass);
        this.key = key;
    }
    
    public VOFilter(Class klass, boolean tryField) {
        this(klass);
        this.tryField = tryField;
    }
    
    public VOFilter(Class klass, String key, boolean tryField) {
        this(klass, key);
        this.tryField = tryField;
    }
    
    private Object createObject() {
        try {
            return klass.newInstance();
        } catch(Exception e) {
            e.printStackTrace();
        }
        return null;
    }
	
    /*
    * Use reflection to set a property in the bean
    */
    private void setValue(Object bean, String name, Object value) {
        // omitted for clarity...
        // use reflection to set a bean value...
        // cache methods and fields for better performance...
        // will try private fields if tryField is true...
    }
	
    public String filter(InvocationChain chain) throws Exception {
        Action action = chain.getAction();
        Input input = action.getInput();
        Object bean = createObject();
        if (bean != null) {
            Iterator iter = input.keys();
            while(iter.hasNext()) {
                String name = (String) iter.next();
                Object value = input.getValue(name);
                if (value == null) continue;
                setValue(bean, name, value);
            }
            // set the bean in the action input
            if (key != null) {
                input.setValue(key, bean);
            } else {
                input.setValue(klass.getName(), bean);
            }
        }
        return chain.invoke();
    }
    
    public void destroy() { }
}
		

Uma action simples que pode utilizar esse filtro é listada abaixo:

package examples.vofilter;

import java.util.*;

import org.mentawai.core.*;

public class HelloVOFilter extends BaseAction {
	
	public String execute() throws Exception {
		User user = (User) input.getValue("user");
		if (user == null) return ERROR;
		if (user.getUsername() == null) return ERROR;
		if (user.getPassword() == null) return ERROR;
		user.setUsername(user.getUsername().toUpperCase());
		user.setPassword(user.getPassword().toUpperCase());
		output.setValue("user", user);
		return SUCCESS;
	}
	
}
		

O código para o ApplicationManager é:

import org.mentawai.core.*;
import org.mentawai.filter.*;

public class ApplicationManager extends org.mentawai.core.ApplicationManager {
	
    public void loadActions() {
        
        // Ruby style
        
        action("/Hello", HelloVOFilter.class)
            .on(SUCESS, fwd("/show.jsp")
            .on(ERROR, fwd("/user.jsp")
            
            .filter(new VOFilter(User.class, "user"));
            
		
        // Java style
        
        ActionConfig ac = new ActionConfig("/Hello", HelloVOFilter.class);
        ac.addConsequence(HelloVOFilter.SUCCESS, new Forward("/show.jsp"));
        ac.addConsequence(HelloVOFilter.ERROR, new Forward("/user.jsp"));
        addActionConfig(ac);
		
        ac.addFilter(new VOFilter(User.class, "user"));
    }
}
		

Note que nós adicionamos um VOFilter que tentará criar e popular o objeto User a partir dos dados de input da action. O objeto será colocado na entrada da action pelo filtro com a chave user, que foi passada no construtor do filtro.

O arquivo user.jsp está logo abaixo. É somente uma form com campos de usuário e senha.

<html>
<body>
<form action="Hello.mtw" method="post">
Username: <input name="username" size="25"><br>
Password: <input type="password" name="password" size="25"><br>
<input type="submit" value="Send">
</form>
</body>
</html>			
			

O arquivo show.jsp está logo abaixo. Ele utiliza uma tag do Mentawai bastante fácil e prática para exibir os atributos de um objeto que está no output da action.

<%@ taglib uri="/WEB-INF/lib/mentawai.jar" prefix="mtw" %>
<html>
<body>
<h3>Hello Value Object Filter!</h3>
<mtw:bean value="user">
<h4>Username: <mtw:out value="username" /></h4>
<h4>Password: <mtw:out value="password" /></h4>
</mtw:bean>
</body>
</html>