Mentawai Web Framework

Filters

A filter intercepts an action so it can modify its input and output, before and after the action is executed. Filters can be very useful for validation, authentication, value objects, file upload, etc. Mentawai comes with ready-to-use filters in the package org.mentawai.filter that you can use to accomplish most of your project needs. However it is important to understand how this fundamental mechamism of the framework works. You may also need to write your own filters to be applied to your own actions.

In mentawai you define filters in the ApplicationManager class, the same way you define ActionConfigs. To learn about filters, let's code a generic filter that will try to populate an object from the action input parameters. You can click here to download this project if you want to. Also note that Mentawai already comes with the ready-to-use and complete org.mentawai.filter.VOFilter, so you don't need to code this in a real project. Refer to the VOFilter section for more details.


The Filter interface

The filter interface has two methods:

package org.mentawai.core;

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

An action may have more than one filter, so for each action an InvocationChain is created. The class InvocationChain has two important methods:

  • getAction(): which returns the action of the invocation chain.


  • invoke(): which executes the next step in the invocation chain, in other words, the next filter or the next action. The action is the last step in an InvocationChain


It is important to understand that a filter can alter an action before and after it is executed. Check the code below for an example:

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() { }
}
		

Let's code our useful generic value object filter, so you can understand how easy it is to work with mentawai filters.


The AfterConsequenceFilter interface

It is also possible to create a filter that performs a task after the action consequence is executed. For example, the filter org.mentawai.filter.ConnectionFilter will only return the connection to the pool after the action consequence is executed, so that the connetion is available while the JSP (view) is being processed.

The interface org.mentawai.core.AfterConsequenceFilter is listed below:

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

The method afterConsequence will be called by the filter after the consequence is executed. Notice that this method is called even if an exception is thrown and the consequence is not completed executed.


The Value Object Filter

The code below will get all values from the action input and try to inject in an object defined in the filter constructor. After this operation is completed, the object will be placed in the action input with the key that was also defined in the filter constructor. If the key was not given the name of the object class will be used as the input key.

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() { }
}
		

The code for a simple action that might use this filter is listed below:

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;
	}
	
}
		

The code for the ApplicationManager is:

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 that we added a VOFilter that will try to create a User object with the action input parameters. The object will be placed by the filter in the input with the key examples.vofilter.User, which is the name of the class.

The user.jsp file is listed below. It is just a form that has an username and passoword field.

<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>			
			

The show.jsp file is listed below. It uses a very convenient mentawai tag for displaying the attributes of an object.

<%@ 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>