Reason for use

I have recently found the need for my Interceptors and Validators to be able to access Components - such as a Validators which is UserAware and checks the UserManager to see if the user exists. Or a Interceptor which is ApplicationAware and asks the ApplicationManager if it is setup yet - if not, then redirecting to a setup action instead.

Currently WebWork (at version 2.1.7) only supports component management of Action, but this can be changed quite easily - if you know where to look.

Extending the Object Factorys

WebWork uses a com.opensymphony.xwork.ObjectFactory object instance to generate the various objects that WebWork utilises - Validators, Interceptors, Actions, and Results for example. This is the object we are going to extend to add some of this functionality.

The methods buildInterceptor and buildValidator do what they say on the tin. I have overriden them to do the following:

public Interceptor buildInterceptor(InterceptorConfig ic, Map map) throws ConfigurationException {
        Interceptor i = super.buildInterceptor(ic, map);
        cm.initializeObject(i);
        return i;
    }

    public Validator buildValidator(String string, Map map) throws Exception {
        Validator v = super.buildValidator(string, map);
        cm.initializeObject(v);
        return v;
    }

Creating a Component Mananger

The variable cm is a ComponentManager. As I am unsure of how to access the ComponentManager that is used in the ComponentInterceptor (or used when initalizing Action objects), we have to create our own. As the ObjectFactory is a singleton the overhead of this is relatively minor, even though not ideal.

The ComponentManager is created in the constructor like this:

private static final Log log = LogFactory.getLog(ObjectFactory.class);

    private ComponentConfiguration cc;
    private ComponentManager cm;

    public ObjectFactory() {
        super();
        cm = (ComponentManager) ActionContext.getContext().get( ComponentInterceptor.COMPONENT_MANAGER );

        if (cm == null) {
            cc = new ComponentConfiguration();
            InputStream configXml = Thread.currentThread().getContextClassLoader().getResourceAsStream("components.xml");
            try {
                cc.loadFromXml(configXml);
            } catch (Exception e) {
                log.info("No component.xml found. They test will continue without initializing components.");
                cc = null;
            }

            cm = new DefaultComponentManager();
            if (cc != null) {
                cc.configure(cm, "session");
                cc.configure(cm, "application");
                cc.configure(cm, "request");
            }
        }
    }

Using our new ObjectFactory

The ObjectFactory is a singleton which allows you to set the object it hands out. To do this I have chosen to override the init method of the com.opensymphony.webwork.dispatcher.ServletDispatcher class. The method looks something like this:

public void init(ServletConfig servletConfig) throws ServletException {
        ObjectFactory.setObjectFactory( new planb.jobsite.xwork.ObjectFactory() );
        super.init(servletConfig);
    }

Code Results

The following full files result from this article.

Object Factory

ObjectFactory.java
import com.opensymphony.xwork.interceptor.Interceptor;
import com.opensymphony.xwork.interceptor.component.ComponentManager;
import com.opensymphony.xwork.interceptor.component.DefaultComponentManager;
import com.opensymphony.xwork.interceptor.component.ComponentInterceptor;
import com.opensymphony.xwork.interceptor.component.ComponentConfiguration;
import com.opensymphony.xwork.config.entities.InterceptorConfig;
import com.opensymphony.xwork.config.ConfigurationException;
import com.opensymphony.xwork.validator.Validator;
import com.opensymphony.xwork.ActionContext;

import java.util.Map;
import java.io.InputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ObjectFactory extends com.opensymphony.xwork.ObjectFactory {

    private static final Log log = LogFactory.getLog(ObjectFactory.class);

    private ComponentConfiguration cc;
    private ComponentManager cm;

    public ObjectFactory() {
        super();
        cm = (ComponentManager) ActionContext.getContext().get( ComponentInterceptor.COMPONENT_MANAGER );

        if (cm == null) {
            cc = new ComponentConfiguration();
            InputStream configXml = Thread.currentThread().getContextClassLoader().getResourceAsStream("components.xml");
            try {
                cc.loadFromXml(configXml);
            } catch (Exception e) {
                log.info("No component.xml found. They test will continue without initializing components.");
                cc = null;
            }

            cm = new DefaultComponentManager();
            if (cc != null) {
                cc.configure(cm, "session");
                cc.configure(cm, "application");
                cc.configure(cm, "request");
            }
        }
    }

    public Interceptor buildInterceptor(InterceptorConfig ic, Map map) throws ConfigurationException {
        Interceptor i = super.buildInterceptor(ic, map);
        cm.initializeObject(i);
        return i;
    }

    public Validator buildValidator(String string, Map map) throws Exception {
        Validator v = super.buildValidator(string, map);
        cm.initializeObject(v);
        return v;
    }

}

Servlet Dispatcher

ServletDispatcher.java
import com.opensymphony.xwork.ObjectFactory;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;

public class ServletDispatcher extends com.opensymphony.webwork.dispatcher.ServletDispatcher {

    public void init(ServletConfig servletConfig) throws ServletException {
        ObjectFactory.setObjectFactory( new planb.jobsite.xwork.ObjectFactory() );
        super.init(servletConfig);
    }

}

web.xml

Replace the reference to the webwork ServletDispatcher to point to the above ServletDispatcher class.

Important Notes

You should find your Interceptors and Validators are now componentized just like Actions, however there are some important notes to be made.

Lifecycle Issues

Interceptors and Validators are both cached by webwork and reused instead of being reinstanciated - this will mean that you may experiance issues with components outside of the application scope. As all of my Interceptor / Validator required components are in this scope, this isn't an issue to me.

One solution to this constraint would be to investigate how webwork caches its Interceptors and Validators, then check to see if the objects use session / request scoped components and cache accordingly. Maybe a thought for the guys planning the next release of webwork!

Conclusion

For now this concludes this article - feel free to add your ideas!