Wednesday, February 3, 2010

Custom Portlet Scope for Spring, JSF, RichFaces portlet Applications

It is all to usual scenario. Create a Portlet, create a Spring bean with the scope="session" or scope="globalSession" and hope that everything works just fine. And it does, until you don't have the scenario where the user creates two separate instance of the same portlet definition.

In that case, all you get is two portlets which display the same informations. Why? Because both of the portlet instances are getting their data from the same Backing Bean instance.

One would wonder, should one use the Spring DispatcherPortlet. If yes, how do I put all that together with JSF, RichFaces etc. At least I did not manage to find any tutorial or example how to put all that technologies together.

So, are we using the right technologies? Still the Answear is most probably yes!

Looking for a soulution, I came accross the information, that one can define his own scope for the Spring Beans.

So I decidet to create own custom scope that saves the Spring Beans per portlet Instance. In other words, each Spring Bean Instance is saved in the PortletSession.PORTLET_SCOPE of the session.

Here is the Custom Scope definition.


public class PortletSessionScope extends AbstractRequestAttributesScope {

private final int scope;


public PortletSessionScope() {
this.scope = PortletSession.PORTLET_SCOPE;
}

@Override
protected int getScope() {
return this.scope;
}

public String getConversationId() {
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
return sessionId;
}


@Override
public Object get(String name, ObjectFactory objectFactory) {
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
Object mutex = requestAttributes.getSessionMutex();
synchronized (mutex) {
ExternalContext external = FacesContext.getCurrentInstance().getExternalContext();
Object scopedObject = null;
Object session = null;
if (null != external) {
session = external.getSession(false);
if (null != session && session instanceof PortletSession) {

scopedObject = ((PortletSession) session).getAttribute(name, PortletSession.PORTLET_SCOPE);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
((PortletSession) session).setAttribute(name, scopedObject, PortletSession.PORTLET_SCOPE);
}
else if (null != session && session instanceof ServletSessionWrapper) {

scopedObject = ((ServletSessionWrapper) session).getAttribute(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
((ServletSessionWrapper) session).setAttribute(name, scopedObject);
}
}
}

return scopedObject;
}
}

@Override
public Object remove(String name) {

Object mutex = RequestContextHolder.currentRequestAttributes().getSessionMutex();
synchronized (mutex) {
Object scopedObject = super.remove(name);
return scopedObject;
}
}
}
So now that you have the scoped bean, all it takes is to make it known to Spring. That is streight process.

In your applicationContext.xml, where all the beans are ceclared, do the following:














So, with that done, all it takes to get Spring Bean in PorltetSession.PORTLET_SCOPE is a deffinition of a Bean with the above scope, something like this:






That was it. Hope it works for you!