This article is devoted to an upgrade of a common JSF Spring application. Time flies and there is already Java EE 7 platform out and widely used. It's sometimes said that Spring framework has become legacy with appearance of Java EE 6. But it's out of scope of this post. Here I'm going to provide notes about the minimal changes that I found required for the upgrade of the application from JSF 1.2 to 2.1, from JSTL 1.1.2 to 1.2, from Servlet 2.4 to 3.0, from Spring 3.1.3 to 4.0.5, from RichFaces 3.3.3 to 4.3.7. It must be mentioned that the latest final RichFaces release 4.3.7 depends on JSF 2.1, JSTL 1.2 and Servlet 3.0.1 that dictated those versions. This post should not be considered as comprehensive but rather showing how I did the upgrade. See the links for more details.
Jetty & Tomcat
First, I upgraded the application to run with the latest servlet container versions. For development I use maven jetty plugin that had to be upgraded in pom.xml from version 6:
<plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <version>6.1.26</version> <configuration> <connectors> <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector"> <port>8080</port> <maxIdleTime>30000</maxIdleTime> </connector> </connectors> <contextPath>/${project.artifactId}</contextPath> <systemProperties> <systemProperty> <name>org.apache.cocoon.mode</name> <value>dev</value> </systemProperty> </systemProperties> </configuration> </plugin>
to version 9:
<plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.2.1.v20140609</version> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> <webApp> <contextPath>/${project.artifactId}</contextPath> </webApp> <systemProperties> <systemProperty> <name>org.apache.cocoon.mode</name> <value>dev</value> </systemProperty> </systemProperties> </configuration> </plugin>
Besides, I could safely remove the following listeners from web.xml as the corresponding issue was fixed in the latest versions of tomcat:
<listener> <listener-class>com.sun.faces.config.ConfigureListener</listener-class> </listener> <listener> <listener-class>com.sun.faces.application.WebappLifecycleListener</listener-class> </listener>
JSTL
As JSTL 1.2 is a maintenance release and I used only c:set tag, I had nothing to do except updating pom.xml:
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
JSF & Facelets
While you can find useful this extended JSF upgrade guide at stackoverflow, I'm going to show the changes I made below. First, I removed facelets dependency and updated jsf dependencies versions in pom.xml. Initially it was:
<dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>1.2_13</version> </dependency> <dependency> <groupId>javax.faces</groupId> <artifactId>jsf-impl</artifactId> <version>1.2_13</version> </dependency> <dependency> <groupId>com.sun.facelets</groupId> <artifactId>jsf-facelets</artifactId> <version>1.1.15</version> </dependency>
As I use RichFaces, its latest version 4.3.7.Final provides richfaces-bom artifact with required JSF version specified as 2.1.28. Thus, I could simply omit version tags:
<dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-api</artifactId> </dependency> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-impl</artifactId> </dependency>
Second, I updated faces-config.xml to comply JSF 2.1 spec. Initially it was:
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"> <application> <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> <!-- locale-config and resource-bundle here --> </application> <!-- converters here --> <navigation-rule> <!-- navigation-cases here --> </navigation-rule> </faces-config>
I modified the root declaration, removed FaceletViewHandler, added from-view-id to fix validation issue and replaced converters with annotations. Well, actually I didn't use JSF @FacesConverter annotation but used Spring @Component instead. Similarly, all managed beans were annotated with @Controller before so I didn't have to add @ManagedBean annotations during the upgrade. Using Spring annotations is allowed as a replacement for both JSF 1.2 and 2.0 thanks to SpringBeanFacesELResolver. As a matter of fact, Spring and JSF 2 are heavily overlapping regarding the annotation support. Finally, this is the new version:
<faces-config version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd"> <application> <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> <!-- locale-config and resource-bundle here --> </application> <navigation-rule> <from-view-id>*</from-view-id> <!-- navigation-cases here --> </navigation-rule> </faces-config>
Third, I modified web.xml to get rid of Facelets 1.x related context-param tags. Initially it was:
<context-param> <param-name>facelets.RECREATE_VALUE_EXPRESSION_ON_BUILD_BEFORE_RESTORE</param-name> <param-value>false</param-value> </context-param> <context-param> <param-name>facelets.LIBRARIES</param-name> <param-value>/WEB-INF/springsecurity.taglib.xml</param-value> </context-param>
As you can see, I use Spring Security taglib there that should be still declared:
<context-param> <param-name>javax.faces.FACELETS_LIBRARIES</param-name> <param-value>/WEB-INF/springsecurity.taglib.xml</param-value> </context-param>
Fourth, the taglib springsecurity.taglib.xml should be modified as well. I won't paste the code here as the change just consists in renaming two Spring classes: Jsf12FaceletsAuthorizeTagHandler to FaceletsAuthorizeTagHandler and Jsf12FaceletsAuthorizeTagUtils to FaceletsAuthorizeTagUtils. You can find the final version of the taglib in spring samples here.
Finally, I replaced head with h:head and body with h:body in all xhtml files.
Servlet
To upgrade Servlet, first I updated pom.xml as usual. Initially it was:
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency>
As I use RichFaces, the servlet version is already specified as 3.0.1 in its bom artifact. Thus, I can simply add the dependency without version tag:
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency>
Then I updated the root declaration of web.xml. Below you can see both versions:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
Finally, as there is a custom listener in the application, I added @WebListener annotation to that class and removed the listener from web.xml.
Spring framework
As for the Spring framework upgrade, the changes were absolutely minimal in my case. First, I updated Spring versions in pom.xml including Security, Web Services and Web Flow dependencies. Initially it was:
<properties> <spring.version>3.1.3.RELEASE</spring.version> <spring.security.version>3.1.3.RELEASE</spring.security.version> <spring.ws.version>2.1.1.RELEASE</spring.ws.version> <spring.webflow.version>2.3.1.RELEASE</spring.webflow.version> </properties>
The updated code is:
<properties> <spring.version>4.0.5.RELEASE</spring.version> <spring.security.version>3.2.4.RELEASE</spring.security.version> <spring.ws.version>2.2.0.RELEASE</spring.ws.version> <spring.webflow.version>2.4.0.RELEASE</spring.webflow.version> </properties>
Second, it appeared wsdl4j:wsdl4j:1.6.1 artifact was required but missing after the upgrade so I added it to the dependencies.
Finally, I replaced versioned schema locations with non-versioned in root declarations of all Spring configuration files.
RichFaces
The upgrade of RichFaces from 3.3.3 to 4.3.7 appeared to be very troublesome as I had to fix many issues afterwards. The official migration guide helps but does not cover every detail. First, I updated pom.xml from:
<properties> <richfaces.version>3.3.3.Final</richfaces.version> </properties> <dependencies> <dependency> <groupId>org.richfaces.framework</groupId> <artifactId>richfaces-impl</artifactId> <version>${richfaces.version}</version> </dependency> <dependency> <groupId>org.richfaces.ui</groupId> <artifactId>richfaces-ui</artifactId> <version>${richfaces.version}</version> </dependency> </dependencies>
to:
<properties> <richfaces.version>4.3.7.Final</richfaces.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.richfaces</groupId> <artifactId>richfaces-bom</artifactId> <version>${richfaces.version}</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.richfaces.core</groupId> <artifactId>richfaces-core-impl</artifactId> </dependency> <dependency> <groupId>org.richfaces.ui</groupId> <artifactId>richfaces-components-ui</artifactId> </dependency> </dependencies>
Second, I modified web.xml:
- removed context-param with name org.ajax4jsf.VIEW_HANDLERS
- removed RichFaces filter and corresponding filter-mapping
Third, there were many RichFaces component changes, so I did the following:
- changed namespace from xmlns:rich="http://richfaces.ajax4jsf.org/rich" to xmlns:rich="http://richfaces.org/rich"
- renamed rich:toolTip to rich:tooltip and removed attribute direction="top-right"
- renamed rich:toolBar to rich:toolbar and rich:toolBarGroup to rich:toolbarGroup
- renamed rich:modalPanel to rich:popupPanel
- renamed attribute value to label of rich:menuItem
- renamed attribute reRender to render
- replaced attribute width="x" with style="width: x" on rich:dataTable and rich:column
- replaced Richfaces.showModalPanel('x') with #{rich:component('x')}.show();
- replaced Richfaces.hideModalPanel('x') with #{rich:component('x')}.hide();
- replaced rich:componentControl[@event="onclick"] with attribute onclick on the target element
- removed attribute selfSorted and changed attribute sortOrder to use enumeration values instead of literals
Thank you for this useful post. I followed this steps and came across the problem which was caused by the version of the following dependency
ReplyDeletecom/ocpsoft/prettyfaces-jsf12/3.3.3/prettyfaces-jsf12-3.3.3.jar
when I replaced it with
com/ocpsoft/prettyfaces-jsf2/3.3.3/prettyfaces-jsf2-3.3.3.jar
for the second version of JSF, the problem has gone.
Thank you