Spring View Scope For JSF 2 Users

In JSF 2 there are several new scopes introduced for managed beans and one of them is view scope, and as any developer who deal with JSF and Spring knows that it is much better to define your JSF managed beans in Spring application context instead of dealing with primitive DI container of JSF itself. Those who know me that I am not using JSF in my own projects any more. However, JSF is used for UI development in a project which I have responsibility on backend side, and in that project JSF managed beans were defined in JSF side. Some of them were scoped with view. When we decided to move those beans into Spring application context, we needed a view scope implementation. When I searched for a ready to use view scope implementation around Internet, I only came up with one or two code pieces which were not supporting life-cycle callbacks from Spring side. Therefore, I decided to implement a new one from scratch and put it here for those who may need it in their own projects as well.

public class ViewScope implements Scope {
	public static final String VIEW_SCOPE_CALLBACKS = "viewScope.callbacks";
	public synchronized Object get(String name, ObjectFactory<?> objectFactory) {
		Object instance = getViewMap().get(name);
		if(instance == null) {
			instance = objectFactory.getObject();
			getViewMap().put(name,instance);
		}
		return instance;
	}
	public Object remove(String name) {
		Object instance = getViewMap().remove(name);
		if(instance != null) {
			Map<string,Runnable> callbacks = (Map<string, Runnable>) getViewMap().get(VIEW_SCOPE_CALLBACKS);
			if(callbacks != null) {
				callbacks.remove(name);
			}
		}
		return instance;
	}
	public void registerDestructionCallback(String name, Runnable runnable) {
		Map<string,Runnable> callbacks = (Map<string, Runnable>) getViewMap().get(VIEW_SCOPE_CALLBACKS);
		if(callbacks != null) {
			callbacks.put(name,runnable);
		}
	}
	public Object resolveContextualObject(String name) {
		FacesContext facesContext = FacesContext.getCurrentInstance();
		FacesRequestAttributes facesRequestAttributes = new FacesRequestAttributes(facesContext);
		return facesRequestAttributes.resolveReference(name);
	}
	public String getConversationId() {
		FacesContext facesContext = FacesContext.getCurrentInstance();
		FacesRequestAttributes facesRequestAttributes = new FacesRequestAttributes(facesContext);
		return facesRequestAttributes.getSessionId() + "-" + facesContext.getViewRoot().getViewId();
	}
	private Map<string,Object> getViewMap() {
		return FacesContext.getCurrentInstance().getViewRoot().getViewMap();
	}
}

As you may have noticed destruction callbacks are also kept in JSF view map. I also made use of Spring’s FacesRequestAttributes in order to give answer for contextual object queries like view, request, session, application etc. Conversation id have to be unique for each view instance, therefore I prefixed current view id with session id so that it became unique accross different users who access same view.

We need a point in order to initialize callbacks map. Fortunately JSF 2 provides a system event facility which notify view map post construction and pre destruction phases. Therefore I also implemented a ViewMapListener in order to initialize callbacks map after view map construction, and execute destruction callbacks just before view map cleanup.

public class ViewScopeCallbackRegistrar implements ViewMapListener {
	public void processEvent(SystemEvent event) throws AbortProcessingException {
		if(event instanceof PostConstructViewMapEvent) {
			PostConstructViewMapEvent viewMapEvent = (PostConstructViewMapEvent)event;
			UIViewRoot viewRoot = (UIViewRoot)viewMapEvent.getComponent();
			viewRoot.getViewMap().put(ViewScope.VIEW_SCOPE_CALLBACKS,new HashMap<string,Runnable>());
		} else if(event instanceof PreDestroyViewMapEvent) {
			PreDestroyViewMapEvent viewMapEvent = (PreDestroyViewMapEvent)event;
			UIViewRoot viewRoot = (UIViewRoot)viewMapEvent.getComponent();
			Map<string,Runnable> callbacks = (Map<string, Runnable>) viewRoot.getViewMap().get(ViewScope.VIEW_SCOPE_CALLBACKS);
			if(callbacks != null) {
				for(Runnable c:callbacks.values()) {
					c.run();
				}
				callbacks.clear();
			}
		}
	}

	public boolean isListenerForSource(Object source) {
		return source instanceof UIViewRoot;
	}
}

Finally we have to configure those two parts in Spring and JSF sides. In order to be able to use view scope, we have to introduce it to Spring in some way. I prefer declarative way. We need to define CustomScopeConfigurer bean in Spring application context, and add “view” scope as new entry.

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="view">
                <bean class="com.example.ViewScope"/>
            </entry>
        </map>
    </property>
</bean>

For the JSF side, we have to register our ViewMapListener in application element of faces-config.xml.

<system-event-listener>
    <system-event-listener-class>com.example.ViewScopeCallbackRegistrar</system-event-listener-class>
    <system-event-class>javax.faces.event.PostConstructViewMapEvent</system-event-class>
    <source-class>javax.faces.component.UIViewRoot</source-class>
</system-event-listener>

<system-event-listener>
    <system-event-listener-class>com.example.ViewScopeCallbackRegistrar</system-event-listener-class>
    <system-event-class>javax.faces.event.PreDestroyViewMapEvent</system-event-class>
    <source-class>javax.faces.component.UIViewRoot</source-class>
</system-event-listener>

Gazi Üniversitesi’nin Teknoloji Günlerindeydik

Geçen cuma günü Gelecex‘in organizatörlüğünde Gazi Üniversitesi’nde düzenlenen teknoloji günlerinde Ant Ivy SVN ve CruiseControl ile Yazılım Geliştirme Yaşam Döngüsü isimli bir sunum gerçekleştirdim. Sunumu isteyen arkadaşlar buradan erişebilirler. Bir dahaki etkinlikte görüşmek üzere…

Subversive mi Subclipse mi

Uzun bir zamandır Eclipse üzerinde SVN ile ilgili işlemlerimi subversive plugini ile gerçekleştiriyordum. Subclipse yerine bu plugin’i tercih etmemin en önemli nedeni olarak sadece UI deneyiminin subclipse’e kısaysla daha hoş olması diyebilirim. Ancak subversive ile çalışırken beni en rahatsız eden durumlardan birisi svn ile komut satırından checkout edilen projelerin Eclipse içerisinde SVN plugin’i tarafından tanınmamasıydı. Son zamanlarda üzerinde çalıştığım build aracı vasıtası ile oluşturulan eclipse projelerinin svn repository’ye otomatik olarak import edilmesi ve hemen ardından checkout edilmesi kabiliyetini ekledikten sonra subversive’in bu problemi daha da can sıkıcı hale geldi. Google’da konu ile ilgili kısa bir araştırma yaptıktan sonra benzer şikayetlerin subclipse ile yaşanmadığını öğrendim. Sanırım bundan sonra subclipse plugin’inin arayüzüne alışmaya çalışacağım…