Integrating Maven 2, Spring and ICEFaces

Last week, I had to turn around a quick and dirty web prototype overlaying a complex piece of pre-written business logic, and on the suggestion of a colleague I tried ICEFaces. Now, there isn’t an abundance of information out there on using these three APIs/frameworks together, but there was enough to cobble together my prototype. So this blog is the drawing together of those blogs and pages I found on the web plus a little bit of the trial and error I went through to get it working.

Fortunately, getting Maven 2 to build ICEFaces and Spring was made simple by a blog posted by Niels van Kampenhout, which can be found here. However, I’ve made a few tweaks, so here is my version of the pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.boggybumblebee</groupId>
	<artifactId>reposearch-icefaces</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>Repository Search Icefaces Web</name>
	<inceptionYear>2008</inceptionYear>
	<dependencies>
		<!-- 3rd Party Test Dependencies -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.4</version>
			<scope>test</scope>
		</dependency>
		<!-- 3rd Party Compile Dependencies -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>2.5.5</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>2.5.5</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.myfaces.core</groupId>
			<artifactId>myfaces-api</artifactId>
			<version>1.2.2</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.myfaces.core</groupId>
			<artifactId>myfaces-impl</artifactId>
			<version>1.2.2</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.icefaces</groupId>
			<artifactId>icefaces</artifactId>
			<version>1.7.0</version>
			<exclusions>
				<exclusion>
					<groupId>javax.el</groupId>
					<artifactId>el-api</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.icefaces</groupId>
			<artifactId>icefaces-comps</artifactId>
			<version>1.7.0</version>
			<exclusions>
				<exclusion>
					<groupId>javax.el</groupId>
					<artifactId>el-api</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
			<scope>compile</scope>
		</dependency>		
		<!-- Internal Compile Dependencies -->
		<dependency>
			<groupId>com.boggybumblebee</groupId>
			<artifactId>reposearch-service</artifactId>
			<version>1.0-SNAPSHOT</version>
			<scope>compile</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>reposearch</finalName>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>tomcat-maven-plugin</artifactId>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-war-plugin</artifactId>
					<configuration>
						<warSourceIncludes>**</warSourceIncludes>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
	<repositories>
		<!—required for the ICEFaces Dependencies -->
		<repository>
			<id>jboss</id>
			<url>http://repository.jboss.com/maven2</url>
		</repository>
	</repositories>
</project>

So once run the mvn install goal against this, you are ready to integrate Spring with ICEFaces.

Next you need to create your WEB-INF/web.xml, or at least modify it from it from its default form, here is mine:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         version="2.4">

	<!-- Context Parameters for Spring -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/context.xml
		</param-value>
	</context-param>

	<!-- Context Parameters for Tomcat -->
	<context-param>
		<param-name>webAppRootKey</param-name>
		<param-value>reposearch</param-value>
	</context-param>

	<!-- Additional Parameters for Tomcat -->
	<session-config>
		<session-timeout>15</session-timeout>
	</session-config>
	
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
	
	<!-- Context Parameters for ICEFaces -->
	<context-param>
		<param-name>com.icesoft.faces.debugDOMUpdate</param-name>
		<param-value>false</param-value>
	</context-param>

	<context-param>
		<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
		<param-value>server</param-value>
		<!-- 
			State saving method: "client" or "server" (= default) See JSF Specification 2.5.2
		 -->
	</context-param>

	<context-param>
		<param-name>com.icesoft.faces.concurrentDOMViews</param-name>
		<param-value>true</param-value>
	</context-param>

	<context-param>
		<param-name>com.icesoft.faces.synchronousUpdate</param-name>
		<param-value>true</param-value>
	</context-param>

	<!-- Listeners for ICEFaces -->
	<listener>
		<listener-class>com.icesoft.faces.util.event.servlet.ContextEventRepeater</listener-class>
	</listener>
 	 
	<!-- Listeners for Spring -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>

	<!-- Faces Servlets -->
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet>
		<servlet-name>Persistent Faces Servlet</servlet-name>
		<servlet-class>
			com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet
		</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet>
		<servlet-name>Blocking Servlet</servlet-name>
		<servlet-class>
			com.icesoft.faces.webapp.xmlhttp.BlockingServlet
		</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Extension mappings -->
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>Persistent Faces Servlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>Persistent Faces Servlet</servlet-name>
		<url-pattern>*.iface</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>Persistent Faces Servlet</servlet-name>
		<url-pattern>/xmlhttp/*</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>Blocking Servlet</servlet-name>
		<url-pattern>/block/*</u>rl-pattern>
	</servlet-mapping>

Once that has been done, all you need to do is create your Spring Context, mine is in WEB-INF/context.xml and looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xsi:schemaLocation="http://www.springframework.org/schema/beans classpath:org/springframework/beans/factory/xml/spring-beans-2.5.xsd" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://www.springframework.org/schema/beans">
	
	<!-- Pre-existing RMI Service -->
	<bean id="service" 
              class="org.springframework.remoting.rmi.RmiProxyFactoryBean" 
              scope="singleton">
		<property name="serviceUrl">
			<value>rmi://localhost:9666/reposearch</value>		
		</property>
		<property name="serviceInterface">
			<value>com.boggybumblebee.reposearch.service.Service</value>
		</property>
		<property name="lookupStubOnStartup">
			<value>false</value>
		</property>
		<property name="refreshStubOnConnectFailure">
			<value>true</value>
		</property>
	</bean>
	
	<!-- ICEFaces Manager Object -->
	<bean id="searchManager" 
              class="com.boggybumblebee.reposearch.icefaces.SearchManager" 
              scope="session">
		<property name="service" ref="service"/>
	</bean>
</beans>

Now the magic is in the scope attributes of both of those beans in the context. The ‘service’ bean is my previously implemented business logic, and for this example it is an RMI Service, but it could be any old bean. Because I don’t want multiple instances of these being created for the application, its scope is ‘singleton’, which for a web application is akin to ‘application’ scope. The ‘searchManager’ bean is specific to an individual user’s session, and is thus given a scope of ‘session’. If this was an unwieldy bean, it could also be given a scope of ‘request’, limiting even further its lifespan. This information is used by the org.springframework.web.context.request.RequestContextListener in the WEB-INF/web.xml and injected into the request at the appropriate scope.

All that is left for you to do is then create your ICEFaces Manager Bean and your JSP page containing your ICEFaces markup and you will have your integartion sorted. Rather than duplicate these steps (and further bloat this blog), you can find plenty of information on this area on a post by Henry Roswell at The Server Side.

I hope this is factually correct, but if anyone has any further suggestions, I would be happy to hear them.