Dashboard > Spring JSF Integration > Home
  Spring JSF Integration Log In View a printable version of the current page.  
  Home
Added by Alef Arendsen, last edited by Ken Weiner on Aug 26, 2005  (view change)
Labels: 
(None)

Candidate list of areas of integration.

Expression and Managed Bean Integration Approach 1

Ok folks, here's the plan:

We propose to link JSF and Spring via AOP to be independent of specific JSF implementations. Our current approach utilizes Spring AOP by providing an ApplicationFactory to JSF. Our Factory supplies JSF with a proxied Application which modifies the behaviour of the createMethodBinding() and createValueBinding() methods. These are the only methods responsible for resolving JSF bean references; for the rest of the methods, we call through to the default Application. The implementation class to be proxied is identified via a .properties file, so it can be changed to match the JSF implementation used. (Currently, we use Sun's Reference Implementation.)

The methods receive the name of the requested bean's property in JavaServer Faces expression language (JSF EL). First, they check whether the requested property can be found within JSF's managed beans. Then, they check whether it can be found within Spring's beans. If there's none of both, the name will be tokenized and evaluated starting with JSF's managed beans. If at some point, there's a property with the name of a Spring bean, the context will be changed from JSF to Spring.

An example:

faces-config.xml (partial):

<managed-bean>
	<description>JSF managed bean referencing a spring bean</description>
	<managed-bean-name>JsfBean</managed-bean-name>
	<managed-bean-class>test.JsfBean</managed-bean-class>
	<managed-bean-scope>session</managed-bean-scope>
</managed-bean>

JsfBean.java:

package test;
public class JsfBean {
	private SpringBean _springBean;

	public JsfBean() {
		super();
	}

	public SpringBean getSpringBean() {
		return _springBean;
	}

	public void setSpringBean(SpringBean paramSpringBean) {
		_springBean = paramSpringBean;
	}
}

applicationContext.xml (partial):

<bean id="SpringBean" class="test.SpringBean">
	<property name="text"><value>some example text</value></property>
</bean>

SpringBean.java:

package test;
public class SpringBean {
	String _text;

	public SpringBean() {
		super();
	}

	public String getText() {
		return _text;
	}

	public void setText(String paramText) {
		_text = paramText;
	}
}

showText.jsp (partial):

<h:outputText value="#{JsfBean.springBean.text}" />

In this example, the proxied Application will first call getSpringBean(). If the returned value is null, it will check whether there's a corresponding bean defined within Spring and call the setter. This way, the JSF managed bean is responsible for caching which enables a developer to use the scope settings of JSF.

Additionally, setApplicationContext() will be called upon instantiation of every JSF managed bean that implements ApplicationContextAware, so that it can interact with Spring if it needs to.

This is what we have right now. Comments and feedback are of course very welcome!

Wolfgang

Expression and Managed Bean Integration Approach 2

Wolfgang's approach is slightly different than the one I'm working on. His also has some different advantages and disadvantages. Not sure which is better but I figured I would put forward my implementation if for no other reason than to provide a different perspective.

My approach was to implement a custom VariableResolver. This new variable resolver could provide 2 features to a Spring + JSF implementation, Spring as an additional simple varialbe context and Direct spring involvement in JSF Managed beans.

Spring as an additional variable context

applicationContext.xml (partial):

<bean id="SpringBean" class="test.SpringBean">
	<property name="text"><value>some example text</value></property>
</bean>

SpringBean.java:

package test;
public class SpringBean {
	String _text;

	public SpringBean() {
		super();
	}

	public String getText() {
		return _text;
	}

	public void setText(String paramText) {
		_text = paramText;
	}
}

showText.jsp (partial):

<h:outputText value="#{SpringBean.text}" />

Given the example above this first feature would allow a JSF binding expression to directly use spring beans from the application context without having to define a Managed bean. Beans used in this approach would not contain any state beyond the scope of the expression. But could be valuable for singletons such as utility classes and stateless event processors. I believe this feature could be added to any JSF implementation in a generic manner by a proxy similar to Wolfgang's approach to proxy the Application above.

Spring bean as a managed bean

faces-config.xml (partial):

<application>
	<variable-resolver>SpringVariableResolver</variable-resolver>
</application>

<managed-bean>
	<description>JSF managed  spring bean</description>
	<managed-bean-name>SpringBean</managed-bean-name>
	<managed-bean-class>test.SpringBean</managed-bean-class>
	<managed-bean-scope>session</managed-bean-scope>
		<managed-property>
			<property-name>text</property-name>
			<value>This is some text</value>
		</managed-property>
</managed-bean>

This feature allows spring beans to be used directly as JSF managed beans. by doing a name match between the <managed-bean-name> tag in the faces-config.xml above and a spring bean name in the application context.

showText.jsp (partial):

<h:outputText value="#{SpringBean.text}" />

When the code sample above is executed then the VariableResolver would check to see if "SpringBean" exists in spring and if it exists as a JSF managed bean as well. If it does VariableResolver will then check to see if the bean already exists in the scope specified by <managed-bean-scope> and returns that instance if it exists. Otherwise, VariableResolver creates a new spring bean (SpringBean), performs the JSF wiring on any managed properties on top of the already wired spring bean, stores the bean in the scope specified, and returns it.

This approach allows for any JSF Managed bean to be a spring bean as well. This bean is then wired by both Spring and JSF. This approach could introduce some confusion because of the double IoC but maybe not.

With this feature there are also some interoperability problems. The main interoperability problem lies in how the JSF implementation retrieves managed bean configuration information and wires up the managed beans. I believe that can be different in each implementation. I don't know if this is a bad idea or not but one possible way around this issue would be for this custom variable resolver to provide it's own faces-context parser and processor.

Here is my VariableResolver implementation as it stands for MyFaces.

SpringVariableResolver.java

import java.util.Map;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import net.sourceforge.myfaces.MyFacesFactoryFinder;
import net.sourceforge.myfaces.config.FacesConfig;
import net.sourceforge.myfaces.config.FacesConfigFactory;
import net.sourceforge.myfaces.config.ManagedBeanConfig;
import net.sourceforge.myfaces.config.configure.ManagedBeanConfigurator;
import net.sourceforge.myfaces.el.VariableResolverImpl;

public class SpringVariableResolver extends VariableResolverImpl {
	public static Log log = LogFactory.getLog(SpringVariableResolver.class);

	public Object resolveVariable(FacesContext context, String name) {
		ServletContext servletContext = 
		    (ServletContext)context.getExternalContext().getContext();
		ApplicationContext appContext =
		    WebApplicationContextUtils.getWebApplicationContext(servletContext);

		// check if bean with same name exists in application context.
		if(!appContext.containsBean(name)) {
			log.info("Spring Bean with name '"+name+"' does not exist allowing MyFaces to process.");
			return super.resolveVariable(context, name);
		} else {
			//obtain JSF configuration information.
			ExternalContext externalContext = context.getExternalContext();
			FacesConfigFactory fcf = MyFacesFactoryFinder.getFacesConfigFactory(externalContext);
			FacesConfig facesConfig = fcf.getFacesConfig(context.getExternalContext());
			ManagedBeanConfig  mbc = facesConfig.getManagedBeanConfig(name);

			//If Managed bean by the specified name exists.
			if (mbc != null) {
				String scopeKey = mbc.getManagedBeanScope().toUpperCase();
				Map scope = null;

				// determine scope of managed bean configuration.
				if("request".toUpperCase().equals(scopeKey)) {
					scope = context.getExternalContext().getRequestMap();
				} else if("session".toUpperCase().equals(scopeKey)){
					scope = context.getExternalContext().getSessionMap();
				} else if("application".toUpperCase().equals(scopeKey)){
					scope = context.getExternalContext().getApplicationMap();
				}

				// check if bean already exists in specified scope
				if(scope != null && scope.containsKey(name)) {
					log.info("Found bean '"+name+"' in scope: "+scopeKey);
					return scope.get(name);
				} else {
					//create new bean for specified scope from spring application context
					ManagedBeanConfigurator configurator = new ManagedBeanConfigurator(mbc);
					Object value = appContext.getBean(name);

					//JSF wire IoC
					configurator.configure(context, value);

					// Store in scope if not "none"
					if(scope != null) {
						scope.put(name, value);
					}
					log.info("Returning JSF Managed spring bean."+name);
					return value;
				}
			} else {
				log.info("Returning spring bean."+name);
				return appContext.getBean(name);
			}
		}
	}
}

In the above example I'm extending MyFaces' existing VariableResolverImpl class however this could easily be changed to a proxy instead.

Anyway, here is another possible approach to a JSF and Spring integration. I must preface my work with the fact that I am very new with both JSF and Spring so there could be several problems with this approach that I'm not aware of so I look forward to comments from those who are more experienced with both.

Mike

Listener or Event Model Integration

I might as well while I'm at it offer up a preliminary and very ugly way to handle the JSF listener tags allowing the use of spring beans as component listeners.

The idea here is to basicly create a Spring-faces tag library to provide a Spring bean listener wrapper to a spring bean.

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
	<bean id="testBean" class="TestBean" singleton="true"/>
	<bean id="testListener" class="TestListener" singleton="false">
		<property name="test">
			<ref local="testBean"/>
		</property>
	</bean>
	<bean id="testSpringListener" class="test.SpringActionListener" singleton="false">
		<property name="beanName">
			<value>testSpringListener</value>
		</property>
		<property name="actionListener">
			<ref local="testListener"/>
		</property>
	</bean>
</beans>

TestListener.java

import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;

public class TestListener implements ActionListener {
    private TestBean testBean;

    public void setTest(TestBean bean) {
    	testBean = bean;
    }

	public void processAction(ActionEvent actionEvent)
	throws AbortProcessingException {
		testBean.hello();
	}
}

test.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/spring_jsf.tld" prefix="spring" %>
<f:view>
	<h:form id="form1">
	    <h:commandButton action="success" binding="#{Page1.button1}" id="button1" value="Test Listener">
	    	<spring:actionListener bean="testListener"/>
	    </h:commandButton>
	</h:form>
</f:view>

SpringActionListenerTag.java

package test;
import javax.faces.FacesException;
import javax.faces.component.ActionSource;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.webapp.UIComponentTag;
import javax.servlet.ServletContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
import javax.faces.event.ActionListener;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class SpringActionListenerTag extends TagSupport {
	private String _bean = null;

	public SpringActionListenerTag() { }

	public void setBean(String bean) {
		_bean = bean;
	}

	public int doStartTag() throws JspException {
		if (_bean == null) {
			throw new JspException("bean attribute not set");
		}

		//Find parent UIComponentTag
		UIComponentTag componentTag = UIComponentTag.getParentUIComponentTag(pageContext);
		if (componentTag == null) {
			throw new JspException("ActionListenerTag has no UIComponentTag ancestor");
		}

		if (componentTag.getCreated()) {

			//Component was just created, so we add the Listener
			UIComponent component = componentTag.getComponentInstance();
			if (component instanceof ActionSource) {
				String beanName;
				FacesContext facesContext = FacesContext.getCurrentInstance();

				//See if bean attribute is an expression and get name if it is.
				if (UIComponentTag.isValueReference(_bean)) {
					ValueBinding vb = facesContext.getApplication().createValueBinding(_bean);
					beanName = (String)vb.getValue(facesContext);
				} else {
					beanName = _bean;
				}
				ServletContext servletContext =
					(ServletContext)facesContext.getExternalContext().getContext();
				ApplicationContext appContext =
					WebApplicationContextUtils.getWebApplicationContext(servletContext);

				//See if bean exists and throw JspException if it does not exists.
				if(appContext.containsBean(beanName)) {
					//get bean to ensure it is an ActionListener
					Object bean = appContext.getBean(beanName);
					if(bean instanceof ActionListener) {
					    if(bean instanceof SpringActionListener) {
					        //If bean specified is already a SpringActionListener
							((ActionSource)component).addActionListener((ActionListener)bean);
					    } else {
					        //wrap listener bean in SpringActionListener 
					        SpringActionListener springListener = new SpringActionListener();
					        springListener.setBeanName(beanName);
					        springListener.setActionListener((ActionListener)bean);
							((ActionSource)component).addActionListener(springListener);
					    }
					} else {
						throw new FacesException("Bean '"+beanName+"' is not of type "+
							ActionListener.class.getName());
					}
				} else {
					throw new FacesException("Bean '"+beanName+"' does not exist in " +
							"Spring Application Context");
				}
			} else {
				throw new JspException("Component " + component.getId() + " is no ActionSource");
			}
		}
		return Tag.SKIP_BODY;
	}
}

spring_jsf.tld

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
	"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">

    <tlib-version>0.0.1</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>Spring JSF Tag Library</short-name>

    <tag>
        <name>actionListener</name>
        <tag-class>test.SpringActionListenerTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>bean</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>
</taglib>

SpringActionListener.java

package test;
import java.util.HashMap;
import java.util.Map;

import javax.faces.component.StateHolder;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.servlet.ServletContext;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class SpringActionListener implements ActionListener, StateHolder {
    
    public static String LISTENER_NAME_KEY = "NAME";
    public static String LISTENER_STATE_KEY = "STATE";

	boolean isTransient = false;
	String beanName;
	ActionListener actionListener;
	Map state;
	
	public SpringActionListener() {
	}
	
	public void setBeanName(String beanName) {
	    this.beanName = beanName;
	}
	
	public String getBeanName() {
	    return beanName;
	}
	
	public void setActionListener(ActionListener actionListener) {
	    this.actionListener = actionListener;
	}
	
	public ActionListener getActionListener() {
	    //return listener if already exists in this instance.
	    if(actionListener != null) {
	        return actionListener;
	    }
	    
	    if(state != null) {
	        beanName = (String)state.get(LISTENER_NAME_KEY);
	    }
		if(beanName == null) {
			throw new IllegalStateException("Listener must have been initialized by a State Manager.");
		}
		//get ActionListener
		ServletContext context =
		    (ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext();
		ApplicationContext appContext =
		    WebApplicationContextUtils.getWebApplicationContext(context);
		actionListener = (ActionListener)appContext.getBean(beanName);
		//restore the state of this ActionListener if it is a StateHolder
		if(state.get(LISTENER_STATE_KEY) != null) {
			if(actionListener instanceof StateHolder) {
			    ((StateHolder)actionListener).restoreState(FacesContext.getCurrentInstance(),
			            state.get(LISTENER_STATE_KEY));
			}
		}
		return actionListener;

	}

	public void processAction(ActionEvent actionEvent) throws AbortProcessingException {
	    //process spring listener.
		getActionListener().processAction(actionEvent);
	}
	
	public boolean isTransient() {
		return isTransient;
	}
	
	public void restoreState(FacesContext context, Object state) {
		this.state = (Map)state;
	}
	
	public Object saveState(FacesContext context) {
	    //If this Listener was never used from a previous saves state
	    if(state == null || actionListener != null) {
	        state = new HashMap();
		    state.put(LISTENER_NAME_KEY, beanName);
		    ActionListener listener = getActionListener();
		    if(listener instanceof StateHolder) {
		        state.put(LISTENER_STATE_KEY, ((StateHolder)listener).saveState(context));
		    }
	    }
		return state;
	}
	public void setTransient(boolean newTransientValue) {
		isTransient = newTransientValue;
	}
}

Unfortunately I know nothing about JSF's event model but this bit of code allowed me to use Spring beans as action listeners so maybe it will help others. This approach could easily be duplicated to implement changeEvent Listeners too.

The way the SpringActionListener is implemented here it could support the wrapping of ActionListeners who implement StateHolder. SpringActionListener itself could also be a spring bean which wraps another listener. If it is then the Spring Tag will store the bean itself as the ActionListener so the following JSP code would work too.

test.jsp using SpringActionListener Bean

<h:commandButton action="success" binding="#{Page1.button1}" id="button1" title="Test" value="Test Listener">
     <spring:actionListener bean="testSpringListener"/>
</h:commandButton>

This approach I believe is JSF implementation generic.

Regards,
Mike

Validator, Converter, and UI Component Integration Approach

Configuring JSF Validators, Converters, and UIComponents in Spring

Hi Mike,

I'm one of the guys who came up with "approach one" together with Wolfgang. We just switched from developing our own open source framework to using spring and JSF, so we're no experts on that field neither. I quite like your approach of double declaring one bean in order to be able to define the scope of a bean (by being JSF managed) instead of having to code two beans. As far as I can see, we should be able to combine our approach (implementation independant, JSF beans can reference spring beans) with your approach quite easily.

Concerning your listener approach: I don't really like having to code a custom tag library and having to teach JSP developers how to use it. I haven't looked into the listener concept of JSF that much, so I don't have an idea at hand yet. I'm also busy working on a project this week, so I won't be able to have a look at it before mid of next week. But I'd be glad if we could find a transparent solution so that developers don't have to bother and ideally don't have to know that there's some glue code integrating both frameworks.

Cheers,
Thomas

I agree it would be better just using the JSF taglib. However, I think a simple spring jsf taglib could be justified if it needed to be. It kind of makes sence to use the <f:actionListener type=""/> if you want to add a class as a listener and <sf:actionListener bean=""/> if you want to use a spring bean as a listener. It's a stretch but I think it would be an acceptable one if there was no other way. I also don't think there would be any difficulty teaching JSP developers to use the taglib. At most it would have 2 tags with one attribute, a very simple taglib. The other justification is although I don't have a whole lot of experience with JSF it would seem that the nested listener tags would be reletively seldom used method of registering an action listener in JSF. It would seem more frequently a progromatic addActionListener would be used or the "actionListener" attribute which would work seemlessly with the VariableResolver.

I'm actually more concerned about the complexity of having to wrap all Action listeners that you want executed in a spring context in a SpringActionListener.

So anyway, food for thought. It would be great if we could come up with a better way....but if not I think the above approach or something similar could be adiquate.

Thanks for the feedback,
Mike

Hi Mike,
I'm another one of the guys who came up with "approach one" together with Wolfgang and Thomas. Just want to say something about your approach connecting JSF and spring via an own implementation (or proxied one) of the VariableResolver. JSF specification says nothing about the whole IoC stuff. To implement a generic or proxied VariableResolver you have to parse the faces-config.xml (because you need to know something about the scope) and you have to write something that wires up your managed-beans (or spring beans managed by JSF). Furthermore it could be a little bit confusing setting bean references on both declared beans (the one declared in spring and the other one declared in JSF using the spring instance and managing the scope). Should JSF override references set up in spring or not...? Nevertheless your approach is a good idea, here's our solution:
A final class called ScopedBean with an java.lang.Object property and package modified accessors.

package de.mindmatters.spring.web.jsf;
public final class ScopedBean {

    private Object _bean;

    public ScopedBean() {
        super();
    }

    Object getBean() {
        return _bean;
    }

    void setBean(Object object) {
        _bean = object;
    }

}

faces-config.xml (partial):

<managed-bean>
	<description>ScopedSpringBean</description>
	<managed-bean-name>ScopedSpringBean</managed-bean-name>
	<managed-bean-class>de.mindmatters.spring.web.jsf.ScopedBean</managed-bean-class>
	<managed-bean-scope>session</managed-bean-scope>
</managed-bean>

applicationContext.xml (partial)

<bean id="ScopedSpringBean" class="de.mindmatters.spring.web.test.SpringBean" singleton="false">
	<property name="userName"><value>Scoped Direct Access!!!</value></property>
</bean>

We implement an aop-proxied VariableResolver letting JSF instantiate the ScopedBean (and check if its of type 'ScopedBean') declared in the faces-config.xml and manage its scope. Because of the package-modified accessors there's no chance to declare references in faces-config.xml for the ScopedBean. Then we check wether the _bean property is null and set the corresponding spring bean identified by the managed-bean-name. If you asked JSF for the ScopedBean (e.g. #{ScopedSpringBean.userName}) the proxied VariableResolver delivers the wrapped SpringBean and not the ScopedBean itself. Of course you can declare other beans (not of type 'ScopedBean') in your faces-config.xml. Management of those beans will be delegated to the 'original' implementation of the specific JSF-VariableResolver. You can mix up this feature with "approach one" posted by Wolfgang.

Cheers, Andreas

Andreas,

I believe your solution is 100% perfect. It's glorious. You're a genius. I can't off hand come up with any problems with your solution. You're solution seems to solves all of the major problems with "approach 2".

Here are a couple of additional thoughts. In approach 2 above I also have the VariableResolver utilizing Spring as an additional variable resolution context. Do you think that would be a valuable feature to add to your VariableResolver Interceptor? It would be very simple to check the spring context for the bean name if the VariableResolver returns null. I can think of a few instances where that functionality could come in handy. What do you think?

Also I wonder if it would be useful to provide in spring some default Spring custom editors for things such as a few of the JSF component types and such to aid in the use of Spring's IoC over JSF's.

Any word on your team's ideas for JSF event integration?

Regards,
Mike

To sum it up and comment my recent development:

Access to spring beans

We've got four ways to access spring beans now:

  1. direct access (handled by a proxied VariableResolver)
    You can access beans that are defined in spring's application context only by using the bean's id. Based on the value of the singleton attribute, the bean will be instantiated for each access or will have application scope.
  2. scoped access (handled by a proxied VariableResolver)
    By defining a bean in spring's application context and in faces-config (using ScopedBean with the same name), you can access the bean defined in springs's application context with the scope defined in faces-config.
  3. referenced access (handled by a proxied PropertyResolver)
    When a bean defined in faces-config has a property that is null and there's a bean in spring's application context that has the same name as the property, the property will be initialized with the spring bean.
  4. managed-property access
    A bean defined in faces-config has a property which shall contain a reference to a bean in spring's application context. You can just use a managed-property to set the reference via direct access (see above). After instantiation, the bean can access the spring bean.

All four variants work already. Variant 3 and 4 both address the problem of JSF beans referencing spring beans. Each has it's own dis-/advantages:

Variant Advantages Disadvantages
3. easy to implement - just add a property to get a reference to a sping bean the id of the bean in spring's application context has to be the same as the propertie's name\\\\the reference will not be set until it is access via EL the first time, so it's only useful for references you want to use in the view, not when the business logic relies on the reference
4. more work to implement since you have to define a managed-property in faces-config for each reference in addition to coding the property you have more flexibility in naming your beans since you use a declarative way of wiring them\\\\the reference is se right after instantiation, so business code can use it

I'd favour variant four. Though both could coexist, I'd like to discard 4 due to it's hard wiring of property name and bean id.

ApplicationContextAwareness

Beans defined in faces-config will be checked whether they implement ApplicationContextAware upon instantiation by a proxied VariableResolver. If they do, setApplicationContext(ApplicationContext) will be called so that JSF beans can access spring's ApplicationContext.

Events

With the above solutions, we already solved the issue of passing events to spring beans as well:

<h:commandButton value="action" actionListener="#{scopedAccessBean.action}"/>

Say scopedAccessBean is a spring bean which is accessed using ScopedBean. Now when you click on the button, the spring bean's method action(ActionEvent) will be called. The same applies to ValueChangeEvent. I don't know about DataModelListener and PhaseListener, since I still need to investigate on them. As far as I can see it now, each bean that implements those interfaces will be added to the corresponding listener queue by the JSF implementation upon instantiation. If this is the case, we might have to modify (proxy) the code instantiating the spring beans on springframework's side in order to add the listeners the the queues. Anyone?

Messages

Within the beans defined in spring's application context, I can just use

FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("my message"));

to add a message to JSF's message. So errors that happen in those beans can easily be reported/displayed on the web page. I don't know about internal messages of springframework, though. To be honest, I haven't found any message queue. Is there any? This would have to be passed to JSF's message queue then, so that those messages can be displayed on the front end.

Code integration in springframework

We just opened a project on sourceforge (jsf-spring) last week. We've got two possibilities:

  1. Upload our code to our sourceforge project and one of springframework's developers reviews it.
  2. Send some of the developers our code so that it can be integrated into springframework's code base (maybe in the sandbox first).

In any case, we'd like to contribute the bits we have now as soon as possible so that others can comment on them and that JSF integration can be part of the next release.

Cheers,
Thomas

Thomas,

I agree with your assessment above and believe we're almost there. You left a couple of holes in the Event area however.

1. You can associate multiple listeners to a component by nesting a JSF tag. I suggested a work around by creating a spring-jsf taglib to create a proxy listener to a spring bean. See example below.

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/spring_jsf.tld" prefix="spring" %>
<f:view>
	<h:form id="form1">
	    <h:commandButton action="success" binding="#{Page1.button1}" id="button1" value="Test Listener">
	    	<spring:actionListener bean="testListener"/>
	    </h:commandButton>
	</h:form>
</f:view>

The downside of this is the spring integration would require a new taglib. However, I see this as a very small problem since so much of JSF is centered on creating new taglibs for example the creation of custom Validators and Components require custom taglibs. Why not add a taglib for a custom kind of listener? I believe this solution would be better than ignoring the functionality.

2. Utility for adding a spring listener to a component programmatically. I also offer a solution for this above by creating a generic Proxy spring listener that can be created or obtained as a spring initialized property and added to a component. (See my examples and code above)

Can't wait to see your code when you post it.

Regards,
Mike

First off, two amendments to my previous post:

  1. advantages and disadvantages of variant 4 should be swapped in the table
  2. I favor 4 and would like to discard 3

Unfortunately, there's now way of editing comments, or is there?\\\\

Mike,

my thoughts on your comment:

  1. I think what you suggest shouldn't be part of the jsf-spring integration plugin, since it doesn't adapt current JSF functionality but extends it. As far as I understand <c:actionListener/>, you can only specify a fully qualified java type which implements ActionListener and which will be instantiated by JSF when the ActionEvent has to be processed. Your solution would enable developers to register managed beans as ActionListeners which isn't possible using the original tag but should be done using the actionListener attribute of say <h:commandButton/>.
  2. Due to the decoration via AOP, FacesContext.getCurrentInstance().getApplication().getVariableResolver().resolveVariable(FacesContext, String) can be used to resolve beans (spring beans as well) which then can be used in any way, including adding them as listeners to JSF components programmatically.

I think, we made quite good progress in our discussion and Andreas and I should be able to release our code by friday or the beginning of next week.\\\\

Cheers,
Thomas

Thomas,

1. The limitation with only being able to use the actionListener attribute is you can only register one listener per component. The whole point of the <c:actionListener/> is to be able to register multiple actionlisteners to a component in a JSP page. So you're suggesting that we should limit users of a JSF-Spring integration to only allowing them to register one managed bean listener to a component? I don't think that is a good limitation. It seems to me that by creating another taglib we would be extending JSF functionality just as much as the Proxied VariableResolver is extending JSF functionality to include Spring Bean support.

2. I don't understand you're answer here. What I am talking about is being able to add an actionlistener to a component in java code by using the addActionListener(ActionEvent) method of a command component. If you use the FacesContext.getCurrentInstance().getApplication().getVariableResolver().resolveVariable(FacesContext, String) method then the actionListener will not exist as a spring bean when it's state is saved. The reason the actionListener attribute of a command component works is because it is added as a listener of the component during the Tree construction phase of every request. So adding a spring bean to a component where it's not going to be used in the same request will not work.

Does that make sense at all?

Mike

Mike,

  1. You're right that this is a limitation, but it is a limitation of JSF, not a limitation the plugin imposes on developers. You still can use <c:actionListener/> to register more than one ActionListener. But you can't register a managed bean by using the tag. This is standard behaviour of JSF. That's why I think it's beyond the scope of the plugin to solve this limitation. The plugin's sole purpose is to let developers use everything they know from JSF and provide a way of accessing spring transparently. Extensions to the JSF API (that go beyond the integration with spring) preferably should be discussed on a purely JSF related forum. Don't get me wrong, I think your suggestion definitely is worth being implemented, but I think that such a general extension should be implemented separately, because it's an extension that's not only useful to developers using JSF and spring, but to any developer using JSF.
  2. Maybe I just don't get what you want to achieve. What I meant is that you can obtain an instance of a spring bean by calling resolveVariable(FacesContext, String). If the bean implements ActionListener, you also can add it to a command component's action listeners by calling it's addActionListener(ActionListener) method. If this isn't what you're talking about, it'd be best if you post a simple JSF only example of what you want to do and I then can figure out if this is possible using spring beans via the plugin as well. I think that everything or at least most of what JSF provides is covered with the already discussed methods of integration - unless I missed some major concept of JSF.

Cheers,
Thomas

Thomas,

1. I can see where you're coming from. However, I don't believe the arguments you are providing make sense. These are your arguments as I see them:

a. Argument: JSF created <c:actionListener/> to add multiple action listeners to a component in a JSP page. Therefore that must be the only way you can add multiple action listeners to a component in a JSP page.
a. Rebuttle: If you can show me somewhere in JSF that says the only way you can ever add multiple listeners to a component in a JSP page is through the <c:actionListener/> tag and that extending this functionality by providing another taglib is not allowed by the spec, then I would conceed that the JSF spec would need to be changed to allow this type of functionality.

b. Argument: This extention would involve changing the JSF API.
b. Rebuttle: This change is a pure extention without needing to change anything about the JSF API. It is 100% JSF implementation independent. If someone wishes to add a class as an action listener then they simply use the <c:actionListener/> tag that comes with JSF. If they wish to add a managed Spring bean as an action listener then they use the <spring-jsf:actionListener bean=""/> tag that comes with the Spring JSF integration package.

c. Argument: Such an extention would be valuable to more than just a Spring JSF implementation.
c. Rebuttle: Adding a taglib that has the tag: <spring-jsf:actionListener bean="joe"/> would only be valuable in a Spring JSF integration.

Perhaps you don't understand the example implementation I provide above? I am really confused as to what the problem is with this feature. Perhaps you could express the concept of being able to create implementation independent tags to add different types of action listeners to JSF to the JSF contact Wolfgang mentioned long ago for a second opinion?

2. I will provide an example of what I mean hopefully later today.

Regards,

Mike

Commenting works again! Thank you, Admins!

Mike,

if the custom actionListener tag is implemented using JSF's VariableResolver (perhaps extending the original actionListener tag), you will have a pure JSF solution that can be used by any JSF developer to add managed-beans as action listeners to components, no matter if spring is used via this plugin. But if the plugin is used, the tag can also be used on spring beans, of course. That's what I meant by saying that it's rather an extension to JSF than something that should be included in spring. I'd recommend to either discuss your feature with JSF developers directly to be included in their code base or to open a sourceforge project for an add-on taglib extending the features of JSF, since it no doubt is useful.

Concerning your request for a second opinion: I'm not the master of this plugin. I just contribute ideas, opinion and code, as do all of us. But I discussed this topic with Wolfgang and Andy and they also tend to consider an actionListener extension being more valuable as general JSF extension than as additional feature introduced by this plugin. Again, we don't criticize your feature, we just think it is off topic within the context of integrating spring and JSF.

No offence meant,
Thomas

Thomas,

I think I see where part of the confusion might be coming from with situation 1. Are you thinking that the <spring-jsf:actionListener bean="XXX"/> tag would add a JSF managed mean as an actionListener? I was thinking it would add a Spring bean as a listener not a JSF Managed Bean. Making it so that when the event is fired then the event will be executed inside of a Spring created bean. I agree that if the extension is to be able to add JSF Managed Beans as listeners then that would be out of the scope of a spring-jsf extension. But my solution is to specify the name of a spring bean that is a ActionListener to be obtained when the action is fired and executed inside of the spring bean's context. I don't understand how extending JSF to support a simple spring bean as an action listener would be any different from extending the VariableResolver to support returning SpringBeans in a JSF EL. Does that additional explanation of the feature help to mitigate your concerns about the extension or can you better explain to me your precise concern?

I'm sorry about asking for a thirdparty I was just hoping to get some additional perspective into the discussion since it seemed that our discussing it was going nowhere.

Here is the example of situation 2 that I promised above.

This example does not work without the help of my SpringActionListener class above.

JSFTestPage.java

import javax.faces.component.html.*;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionListener;
import javax.servlet.ServletContext;

import org.springframework.web.context.support.WebApplicationContextUtils;

public class JSFTestPage {
    private HtmlCommandButton button1 = new HtmlCommandButton();
  
    public JSFTestPage() {
        button1.addActionListener((ActionListener)WebApplicationContextUtils.getWebApplicationContext(
            (ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext())
            .getBean("testListener"));
    }
    public HtmlCommandButton getButton1() {
        return button1;
    }
    public void setButton1(HtmlCommandButton button1) {
        this.button1 = button1;
    }
}

testJSF.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="/WEB-INF/spring_jsf.tld" prefix="spring" %>
<f:view>
	<h:messages/> 
	<h:form id="form1">
	    <h:commandButton binding="#{Page1.button1}"  action="success" id="button1"value="Test Listener"/>
	</h:form>
</f:view>

managed-beans.xml

<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
                              "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
	<referenced-bean>
		<referenced-bean-name>org.springframework.web.context.WebApplicationContext.ROOT</referenced-bean-name>
		<referenced-bean-class>org.springframework.context.ApplicationContext</referenced-bean-class>
	</referenced-bean>
	<application>
		<variable-resolver>SpringVariableResolver</variable-resolver>
	</application>
	<managed-bean>
		<managed-bean-name>Page1</managed-bean-name>		
		<managed-bean-class>JSFTestPage</managed-bean-class>
		<managed-bean-scope>session</managed-bean-scope>
	</managed-bean>
	<navigation-rule>
		<from-view-id>/jsf/testJSF.jsp</from-view-id>
		<navigation-case>
			<from-outcome>success</from-outcome>
			<to-view-id>/faces/jsf/testJSF.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>
</faces-config>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
	<bean id="testBean" class="TestBean" singleton="true"/>
	<bean id="testListener" class="TestListener" singleton="true">
		<property name="test">
			<ref local="testBean"/>
		</property>
	</bean>
	<bean id="Page1" class="JSFTestPage" singleton="false"/>
</beans>

TestBean.java

public class TestBean {
	public void hello() {
		System.out.println("Hello world this is a spring bean.");
	}
}

TestListener.java

import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;

public class TestListener implements ActionListener {
    private TestBean testBean;
    public String stateInfo;

    public void setTest(TestBean bean) {
    	testBean = bean;
    }

	public void processAction(ActionEvent actionEvent)
	throws AbortProcessingException {
		testBean.hello();
	}
}

When Page.button1 is pressed it will fire an event to TestListener. However, because the SpringBean TestListener's state is not persisted properly as a spring bean it looses its reference to "TestBean" so when the action is fired we get a NullPointerException. However, if we wrap the TestListener in something similar to my SpringActionListener above before adding it to the Component as a listener then everything works perfectly. The Spring Plugin could provide a utility similar to the SpringActionListener and perhaps a simple factory method to wrap ActionListeners that the developer wants to be fired as a Spring ManagedBean.

Regards,

Mike

I finally did release our current code on sourceforge!

Check out http://sourceforge.net/projects/jsf-spring for the downloads:

  • jsf-spring-1.0.zip contains source code, binary distribution and javadoc
  • jsf-spring-1.0-example.war contains a sample web application using all the features of our code

The javadoc is accessible online at http://jsf-spring.sourceforge.net/


Mike,

just a short comment because it's quite late in Germany already and I gotta go home:

  1. You're right, we don't get anywhere. Of course, your code can only be used to add a Spring bean as an ActionListener. But simplify the tag's code by not accessing Spring's ApplicationContext but using JSF's VariableResolver instead, and you could get rid of it's limitation to only act on Spring beans. It then would be able to act on JSF beans as well. Thus, you would even increase it's usefulness. That's one reason why I think it's better off within pure JSF context. I really don't get why you're insisting on including an extension to JSF (that goes further than just expanding JSF's "innate" capabilities with the ability to access Spring) here. What we're doing here, is glueing - not extending. At least that's what my intention is.
  2. Great you came up with an example. I'll have a look at it tomorrow.

Cheers,
Thomas

Thomas,

1. I see what you're saying and you are right. If you begin thinking of taking the new tag one step further and use the VariableResolver then that would start moving beyond the scope of a simple JSF+Spring integration. It would also present several new problems and features such as session scoped Listeners which is why I didn't want to take it that far. The goal of the tag using only the ApplicationContext was to simply provide a wrapper for Listeners that allowed Events to be triggered inside of a Spring Context and that was it. The goal of my insistence for this feature was to give the user of a Spring and JSF integration what I thought would be a simple utility helping to make their JSF+Spring experience more complete. Anyway, I think we've hashed this issue to bits so I won't bother you about it anymore. This feature will always be here if anyone wishes to use it.

2. You are probably going to have similar exception to this case because it is based on the same assumption that people would want a simple utility allowing them to execute their listeners within a spring context. In addition, it uses the same trick as the tag only it's use is for the case of wanting to add listeners programmatically rather than in the jsp because adding a Spring Bean as a listener programmatically doesn't currently work.

Thanks for taking the time to talk with me through this issue; I'm ready to put it to rest.

Regards,
Mike

Thomas,

Moving on I just downloaded your jsf-spring integration kit and am very impressed, it's great and thanks for the hard work you and your team put into it. I have a couple of comments and question though.

1. I'm sure there is a good reason for this so this is more curiosity than comment. Why did you implement a special ContextLoader/Listener for JSF? To my untrained eye it doesn't appear you're doing anything beyond what the org.springframework.web.context.ContextLoaderListener provides.

2. I was wondering if in the BeanAccessAdvice it might be a good idea to require a scopedBean to NOT be a singleton. I can't personally think of any cases where one would want a singleton scoped bean. Plus to my understanding JSF Managed beans not being singletons would seem to fit in better with JSF thinking.
3. I'm probably revealing my ignorance again but what is the reason for the AwareAdvice? Doesn't spring set all of the "*Aware" interfaces you are already? The only reason I could think for us to want to reset them is in the case of some of them changing between requests but it doesn't appear any of the *Aware interfaces you're working with in AwareAdvice would fall into that category.
4. Do you think it would be useful for the Spring-jsf integration to add a FacesContextAware interface?
5. I see that you added a Filter for propagating RequestHandlerEvents. I noticed in your javadocs that you note: "RequestHandledEvent should be published to Spring according to the specification." Where in the specification are you referring to? I found a note in the Reference Manual 3.10.2 that says: "Note that this event is only applicable for web applications using Spring's DispatcherServlet". Does that still apply for this integration? Do you think that is saying that we don't need to publish this event unless we're using the DispatcherServlet?

Thanks again.

Regards,
Mike

Hi Mike,

1. We extend the original ContextLoader and add a factory method that returns the loaded application context, without the need of a ServletContext (see: WebApplicationUtils class)
2. Your scoped spring bean should not to be declared as a singleton, but nevertheless you could do...
3. The AwareAdvice utilizes JSF-managed-beans. So your JSF-managed-beans could also implement the "*Aware" interfaces.
4. We think about that approach last week...But there are problems with referenced spring beans - if a referenced bean implements the FacesContextAware interface, we could not easily update the context although we have to because the FacesContext is request-dependent...
5. Well a JSF-application is a web application...why not provide this feature although we do not use the DispatcherServlet. You don't need to deploy the RequestHandledServlet if don't need this feature.

Andreas,

1. I see....that's nice.

2. I'm wondering if we should throw an exception if they attempt to use a singleton as a JSF Managed bean. This could cause a lot of confusion because a singleton JSF managed session bean would be the same as an Application scoped bean. It would seem more true to the JSF spec if we didn't allow singletons to be JSF Managed bean. If they want to use a singleton then the integration should make sure that it is not a jsf managed bean too.

3. This makes sence. However it might be confusing letting JSF Managed beans without a spring backing know what their JSF bean name is. It would seem to me that if you're letting pure JSF managed beans implement BeanNameAware that this would be adding a feature to JSF that would be useful outside the scope of a Spring+JSF integration. Perhaps you aught to let Spring handle the BeanNameAware interface and not provide that functionality to JSF Managed Beans? The same argument could possible be made for ServletContextAware but I'm less concerned about that one.

4. This is true, I can't think of an immediately simple way to do this either but it sure would be cool. I'll have to think about it.

5. Correct me if I'm wrong but isn't the DispatcherServlet only for using Spring's MVC framework? So does this mean that your integration allows you to combine JSF with the Spring MVC Framework into a single request? Interesting, I guess I'll have to learn more about Spring MVC now. If this is not the case then I'll again have to wonder at the value of this Filter.

Regards,

Mike

Andreas,

4. I was doing some thinking about #4. Although FacesContextAware could not be implemented for JSF Managed Beans it could be implemented for non JSF Managed Spring Beans obtained through the VariableResolver such as for utility singletons or ActionListeners.

Mike

Mike,

2. Nice suggestion... I will think about it this weekend
3. You are right. I will think about a VariableResolver implementing the ApplicationContext-interface
5. See javaDoc of RequestHandledEvent: "Event raised when a request is handled within a WebApplicationContext. Supported by Spring's own FrameworkServlet, but can also be raised by any other web component."

In my opinion, the main task of the beans defined in Spring's Application Context is providing business logic to the UI layer.
So to integrate JSF Web Layer with Spring Middle Layer,
We just need the way to get the reference of Spring's Application Context from JSF managed-bean.
What classes of your package do we need if we just use the simplest way like above?

Posted by Anonymous at May 10, 2004 01:27

Hi Anonymous,

to get a reference to Spring's ApplicationContext, simply let your JSF managed bean implement org.springframework.context.ApplicationContextAware.

But if you want to use the ApplicationContext to get a reference to a Spring managed bean, I think you would be better off using one of the bean access methods described în the javadoc.

Cheers,
Thomas

Hi Thomas.

Thanks for your reply. Then I will use the the way 4 which you discribed abaove.

Regards,
Meng

Posted by Anonymous at May 11, 2004 02:50

Announcement - We've released a new version of our code on sourceforge: http://jsf-spring.sourceforge.net/

Based on the discussion here (mainly Mikes comment from May 6th) we re-designed everything. Briefly, the new strategy is: Implement a WebApplicationContext that reads the JSF config files. This way, we can simply integrate it into Spring as if it were a Spring context defined in a separate xml file. Actually it is, the xml file just has another syntax, namely that of JSF. The result is, that Spring takes care of most of the initialization work and the availability of Spring's *Aware interfaces can be achieved in a consistent manner.

JSF beans are Spring beans and vice versa, and they can access each other very transparently.
Please have a look at the code and comment on it.

Cheers,
Thomas

Thomas,

Version 2.0 looks very good, however I can't seem to get the example to work. The distribution appears incomplete. When I deploy it and attempt to execute it I get a:

java.lang.IllegalStateException:
initWebApplicationContext(ServletContext) has to be called first! Please use one of org.springframework.web.jsf.ContextLoader's subclasses to do so.
org.springframework.web.jsf.ContextLoader.getWebApplicationContext(ContextLoader.java:105)
org.springframework.web.jsf.RequestHandledFilter.doFilter(RequestHandledFilter.java:111)

Any ideas? I left a bug in your project yesterday but haven't heard anything back about it, perhaps that would be a better place to continue this discussion.

Mike

Hi Mike,

Please check your download version and/or your tomcat/jboss (or whatever you use) - appserver cache, because we do not use anymore our own ContextLoader implementation nor an own ContextLoaderListener/-Servlet. Please read the javaDocs carefully.

Thanks in advance,

Andreas