Skip to main content

Migración JSF1.2, Spring 2.5.6, RichFaces 3.3.3.Final y Primefaces 1.1 a JSF2, Spring 3, RichFaces 4.0.0.Final y Primefaces 2.2.1

· 22 min read

En el presente post se va a describir los pasos necesarios para migrar una aplicacion web de JSF1.2 a JSF2 así como las modificaciones necesarias para migrar Spring, RichFaces y Primefaces.

Librerías dependientes

Además de modificar los jar necesarios para JSF2, deberemos indicar los jar necesarios para el expression language. Las dependencias necesarias en formato pom de maven serían:

<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>2.2</version>
<optional>false</optional>
</dependency>

<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.0.3</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.0.3</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<optional>false</optional>
</dependency>

Spring ya no se distribuye en un solo .jar, por lo que es necesario indicar la relación de jars que necesitamos de spring. En maven, deberíamos reemplazar nuestra entrada spring-2.5.6.jar por lo siguiente:

<dependency>  
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.0.7.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.0.7.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.0.7.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.0.7.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>

Deberíamos utilizar, por compatibilidad, las dependencias de Spring Security 3.0.7.

<dependency>  
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-webflow</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-binding</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-faces</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-js</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-js-resources</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>

Las dependencias de Spring Webflow son:

<dependency>  
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-webflow</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-binding</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-faces</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-js</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-js-resources</artifactId>
<version>2.3.0.RELEASE</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>

Para Richfaces 4, debemos tener en cuenta que aparte de las dependencias relativas al core-api, core-impl, components-iu y components-api, debemos tener en cuenta las nuevas dependencias de google.guava, cssparser y sac.

<dependency>  
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-components-api</artifactId>
<version>4.0.0.Final</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-components-ui</artifactId>
<version>4.0.0.Final</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.richfaces.core</groupId>
<artifactId>richfaces-core-api</artifactId>
<version>4.0.0.Final</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.richfaces.core</groupId>
<artifactId>richfaces-core-impl</artifactId>
<version>4.0.0.Final</version>
<optional>false</optional>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.sourceforge.cssparser</groupId>
<artifactId>cssparser</artifactId>
<version>0.9.5</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.w3c.css</groupId>
<artifactId>sac</artifactId>
<version>1.3</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>r09</version>
<optional>false</optional>
</dependency>

Actualizamos las dependencias de PrimeFaces a 2.2.1

<dependency>  
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>2.2.1</version>
<optional>false</optional>
</dependency>

Modificaciones en el web.xml

El web.xml se ha simplificado y principalmente debemos quitar fichas que ya no hacen faltan con las nuevas versiones de JSF2, RichFaces, PrimeFaces, etc. También se han producido cambios en los nombres de los parámetros que aún se mantienen.

Para RichFaces, hay que eliminar las siguientes entradas, que ya no son necesarias:

<filter>  
<display-name>RichFaces Filter</display-name>
<filter-name>richfaces</filter-name>
<filter-class>org.ajax4jsf.Filter</filter-class>
<init-param>
<param-name>createTempFiles</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>maxRequestSize</param-name>
<param-value>1000000</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>richfaces</filter-name>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>

También hay que cambiar el nombre de parámetros, como por ejemplo, org.richfaces.SKIN que debe ser sustituido por org.richfaces.skin.

También sobran parámetros como:

<context-param>  
<param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
<param-value>com.sun.facelets.FaceletViewHandler</param-value>
</context-param>

Ya no es necesario utilizar ningún servlet para PrimeFaces. Por tanto, debemos eliminar las siguientes fichas:

<servlet>  
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>org.primefaces.resource.ResourceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/primefaces_resource/*</url-pattern>
</servlet-mapping>

Únicamente pondremos fichas relacionadas con PrimeFaces si utilizamos el componente FileUpload. En este caso debemos añadir el siguiente filtro:

<filter>  
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<filter-class>
org.primefaces.webapp.filter.FileUploadFilter
</filter-class>
<init-param>
<param-name>thresholdSize</param-name>
<param-value>51200</param-value>
</init-param>
</filter>

que lo deberemos asociar con el servlet del Dispatch de Spring MVC, por ejemplo, del siguiente modo:

<filter-mapping>  
<filter-name>PrimeFaces FileUpload Filter</filter-name>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
</filter-mapping>

Con lo que respecta a la configuración de JSF2 y faces, las fichas que deberíamos poner son las siguientes:

<context-param>  
<param-name>com.sun.faces.expressionFactory</param-name>
<param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/web-application-config.xml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/springsecurity.taglib.xml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
<!--
<param-value>Production</param-value>
-->
</context-param>
<context-param>
<param-name>javax.faces.REFRESH_PERIOD</param-name>
<param-value>1</param-value>
</context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

Los nombres de los parámetros se mantienen por compatibilidad, pero algunos de ellos han cambiado. Por ejemplo, facelets.DEVELOPMENT ha pasado a ser javax.faces.PROJECT_STAGE. Los nuevos parámetros podemos encontrarlos en:

http://docs.jboss.org/jbossas/6/JSF_Guide/en-US/html/jsf.reference.html

En el caso concreto del parámetro PROJECT_STAGE, hay que decir que si le asignamos el valor Production, en algunas ventanas veremos que en los archivos de log o en la consola del servidor de aplicaciones (por ejemplo, tomcat) aparece el mensaje:

FacesMessage(s) have been enqueued, but may not have been displayed

o en español,

ADVERTENCIA: FacesMessage(s) se han puesto en la cola, pero es posible que no se muestren.

Este mensaje se muestra en ventanas con componentes a4j. El mensaje realmente indica que no se ha podido mostrar un mensaje de error/aviso relacionado con algún componente, aunque en la práctica sí que se muestra en la ventana. Una solución es utilizar el componente rich:messages en lugar de h:messages o poner asignar al parámetro PROJECT_STAGE el valor Development. La segunda solución propuesta tiene el inconveniente de que en el pie de nuestras páginas veremos el texto de los mensajes h:messages.

En cuanto a las fichas relacionas con Spring y Spring Security, podemos decir que más o menos se mantienen. Cabe resaltar que, al contrario de lo que se aconseja en la documentación de Spring, nos hemos visto obligados a quitar el filtro CharacterEncodingFilter debido a que en las ventanas no se mostraban correctamente los caracteres con diacríticos (tildes, diéresis, etc.), a pesar de probar con distintos encoding, UTF-8, ISO-8859-1, etc. Por tanto, se han eliminado las siguientes fichas:

<filter>  
<filter-name>charEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>

Modificaciones en faces-config.xml

En el archivo faces-config.xml actualizaremos la versión de la referencia del xsd y dtd de la versión 1 a la versión 2. Para ello, el archivo ahora comenzará con:

<?xml version="1.0" encoding="UTF-8"?>  
<faces-config 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_0.xsd"
version="2.0">

Se debe añadir la entrada

<application>  
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>

[sourcecode lang="plain"] org.springframework.web.jsf.el.SpringBeanFacesELResolver [/sourcecode]

eliminando previamente

<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>

Reubicación de paquetes y clases

Varias clases de Spring Core y Spring Security han cambiado su ubicación en los paquetes y algunas también, incluso, han cambiado alguno de sus métodos. Algunas de ellas son las siguientes:

Clases de Spring

  • Remplazar org.springframework.security.userdetails.UserDetails por org.springframework.security.core.userdetails.UserDetails
  • Remplazar org.springframework.security.userdetails.UsernameNotFoundException por org.springframework.security.core.userdetails.UsernameNotFoundException
  • Remplazar org.springframework.security.context.SecurityContext por org.springframework.security.core.context.SecurityContext
  • Remplazar org.springframework.security.context.SecurityContextHolder por org.springframework.security.core.context.SecurityContextHolder
  • Remplazar org.springframework.security.providers.AbstractAuthenticationToken por org.springframework.security.authentication.AbstractAuthenticationToken
  • Remplazar org.springframework.security.AccessDeniedException por org.springframework.security.access.AccessDeniedException
  • Remplazar org.springframework.security.Authentication por org.springframework.security.core.Authentication
  • Remplazar org.springframework.security.providers.UsernamePasswordAuthenticationToken por org.springframework.security.authentication.UsernamePasswordAuthenticationToken
  • Remplazar org.springframework.security.GrantedAuthority por org.springframework.security.core.GrantedAuthority
  • Remplazar org.springframework.security.GrantedAuthorityImpl por org.springframework.security.core.authority.GrantedAuthorityImpl

 El método getAuthorities() de la clase UserDetails ha cambiado y ahora ya no devuelve un GrantedAuthority[], sino un Collection, por lo que habrá que realizar los correspondientes cambios en aquellas clases donde tengamos implementado este método.

Clases de RichFaces

  • Remplazar org.richfaces.event.UploadEvent por org.richfaces.event.FileUploadEvent
  • Remplazar org.richfaces.model.UploadItem por org.richfaces.model.UploadedFile
  • Remplazar org.richfaces.component.html.HtmlDataGrid por org.richfaces.component.UIDataGrid
  • Remplazar org.richfaces.component.html.HtmlDatascroller por org.richfaces.component.UIDataScroller
  • Remplazar org.richfaces.component.html.HtmlExtendedDataTable por org.richfaces.component.UIDataScroller
  • Remplazar org.richfaces.component.html.HtmlInplaceInput por org.richfaces.component.UIInplaceInput

Debido al cambio de UploadEvent y UploadItem hay que modificar los métodos listener que tengamos asociados con la subida de archivos en nuestras clases. Estas dos clases han cambiado sus métodos y la forma que gestionan el archivo que se acaba de subir. Ahora deberemos tratar el archivo que se ha subido con los métodos relacionados con un array de bytes o como un InputStream. Por ejemplo, un código válido para tratar los datos del archivo que se recibe sería:

public void listenerUploadFile(FileUploadEvent event) throws IOException {  
UploadedFile item = event.getUploadedFile();
File output = new File(item.getName());
FileOutputStream fos = new FileOutputStream(output);
fos.write(item.getData());
file.setName(item.getName());
file.setFile(output);
}

o, tratando el InputStream, apoyándonos en la clase IOUtils de org.apache.commons.io

public void listenerUploadFile(FileUploadEvent event) throws IOException {  
UploadedFile item = event.getUploadedFile();
FileOutputStream fos = null;
try {
currentFile = File.createTempFile("prefix", "suffix");
fos = new FileOutputStream(currentFile);
IOUtils.copy(item.getInputStream(), fos);
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage(), e);
}finally {
IOUtils.closeQuietly(fos);
}
}

Las soluciones mostradas son similares pare el componente equivalente fileUpload de PrimeFaces.

Dejan de ser operativas distintas  clases de ajax4jsf en JSF2. Así, por ejemplo, las sentencias como las siguientes deben ser comentadas o anuladas, ya que sólo se deben utilizar en JSF1.2.

import org.ajax4jsf.component.UIAjaxSupport;
import org.ajax4jsf.component.html.HtmlAjaxSupport;
import org.ajax4jsf.taglib.html.jsp.AjaxSupportTag;

Componentes JSF2

Como era de esperar, han cambiado métodos de los componentes de JSF2. Han cambiado el número de parámetros de los métodos render. Por ejemplo, ahora si extendemos el componente básico de un radio buttons, tendremos que tener en cuenta que el método renderOption tiene un parámetro más: OptionComponentInfo optionInfo.

Configuraciones Spring

Tendremos que tocar varios archivos de configuración Spring, principalmente, los relativos a la configuración general de bean, web-application-config.xml, a los relativos a MVC, normalmente webmvc-config.xml, a la configuración de los flujos SWF de Spring, normalmente webflow-config.xml, el archivo de seguridad, normalmente security-config.xml, el archivo de configuración de acceso a base de datos, data-access-config.xml, y los archivos donde se definen los estados y acciones de los flujos. Los nombres de los archivos son los mismos y con la misma finalidad que los que se utilizan en el ejemplo booking-faces que los tutoriales de Spring ofrecen.

Las modificaciones realizadas en estos archivos se han llevado a cabo siguiendo lo indicado en:

http://static.springsource.org/spring-webflow/docs/2.3.x/reference/htmlsingle/spring-webflow-reference.html

y en

http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity-single.html

web-application-config.xml

Este archivo de configuración debe tener actualizadas las versiones de los xsd. Por tanto, deberá comenzar del siguiente modo:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

webmvc-config.xml

Este archivo de configuración debe tener actualizadas las versiones de los xsd. Por tanto, deberá comenzar del siguiente modo:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:faces="http://www.springframework.org/schema/faces"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces-2.2.xsd">

La siguiente ficha debe ser:

<faces:resources />  

Si no se pusiera esta ficha, obtendríamos errores del estilo:

  • java.io.FileNotFoundException: /WEB-INF/javax.faces.resource/jsf.xhtml Not Found in ExternalContext as a Resource
  • Si utilizásemos tags de primefaces del tipo tendríamos el error
    java.io.FileNotFoundException: /WEB-INF/javax.faces.resource/dot_spacer.xhtml Not Found in ExternalContext as a Resource
  • java.io.FileNotFoundException: /WEB-INF/rfRes/skinning.xhtml Not Found in ExternalContext as a Resource

Para que funcione correctamente la ficha antes mencionada, es necesario añadir la declaración del siguiente bean:

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />  

Si no se declarase este bean, se producirían errores del tipo

`javax.servlet.ServletException: No adapter for handler [org.springframework.webflow.mvc.servlet.FlowController@5e526300]: Does your handler implement a supported interface like Controller?``

La propiedad ajaxHandler del bean flowController ya no debe estar asignada a la clase org.springframework.faces.richfaces.RichFacesAjaxHandler, sino a org.springframework.faces.webflow.JsfAjaxHandler. El bean de flowController quedará como:

<bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">  
<property name="flowExecutor" ref="flowExecutor"/>
<property name="ajaxHandler">
<bean class="org.springframework.faces.webflow.JsfAjaxHandler"/>
</property>
</bean>

Si no hiciéramos este cambio, obtendríamos errores del tipo java.lang.ClassNotFoundException: org.ajax4jsf.Filter

En la propiedad mappings del bean org.springframework.web.servlet.handler.SimpleUrlHandlerMapping, que se encarga de relacionar uris con clases controllers, debemos añadir la ficha

<prop key="/rfRes/**">jsfResourceHandlerHack</prop>

Por ello, también es necesario añadir la declaración del siguiente bean:

<bean name="jsfResourceHandlerHack" class="org.springframework.faces.webflow.JsfResourceRequestHandler" />  

Si no se pusiera la propiedad con key rfRes mencionado obtendríamos también el error citado anteriormente con patrón

java.io.FileNotFoundException: /WEB-INF/rfRes/ Not Found in ExternalContext as a Resource

webflow-config.xml

Este archivo de configuración debe tener actualizadas las versiones de los xsd. Por tanto, deberá comenzar del siguiente modo:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xmlns:faces="http://www.springframework.org/schema/faces"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd
http://www.springframework.org/schema/faces
http://www.springframework.org/schema/faces/spring-faces-2.2.xsd">

Al bean flowExecutor le debemos añadir el listener facesContextListener.

Un ejemplo de flowExecutor podría y los beans dependientes que deberíamos declarar sería:

<webflow:flow-executor id="flowExecutor" >  
<webflow:flow-execution-repository max-executions="2"/>
<webflow:flow-execution-attributes>
<webflow:always-redirect-on-pause value="true"/>
</webflow:flow-execution-attributes>
<webflow:flow-execution-listeners>
<webflow:listener ref="facesContextListener"/>
<webflow:listener ref="jpaFlowExecutionListener" />
<webflow:listener ref="securityFlowExecutionListener" />
</webflow:flow-execution-listeners>
</webflow:flow-executor>
<!-- Configures the Spring Web Flow JSF integration -->
<faces:flow-builder-services id="facesFlowBuilderServices" />
<!-- Installs a listener that creates and releases the FacesContext for each request. -->
<bean id="facesContextListener" class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener"/>
<!-- Installs a listener that manages JPA persistence contexts for flows that require them -->
<bean id="jpaFlowExecutionListener" class="org.springframework.webflow.persistence.JpaFlowExecutionListener">
<constructor-arg ref="entityManagerFactory" />
<constructor-arg ref="transactionManager" />
</bean>
<!-- Installs a listener to apply Spring Security authorities -->
<bean id="securityFlowExecutionListener" class="org.springframework.webflow.security.SecurityFlowExecutionListener" />

security-config.xml

Este archivo de configuración debe tener actualizadas las versiones de los xsd. Por tanto, deberá comenzar del siguiente modo:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

El principal cambio que hay que realizar es poner el bean del autentication-provider dentro del nuevo bean authentication-manager que ahora permite tener más de un authentication-provider. Un ejemplo del nuevo authentication-manager sería:

<security:authentication-manager>  
<security:authentication-provider user-service-ref="userDetailsService" >
<security:password-encoder hash="md5"/>
</security:authentication-provider>
</security:authentication-manager>

data-access-config.xml

Este archivo de configuración debe tener actualizadas las versiones de los xsd. Por tanto, deberá comenzar del siguiente modo:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

Archivos de flujos

En los archivos de flujos deberemos sustituir las macros ${} por #{}, ya que esta modificación está la sintaxis que utiliza Spring EL, tal y como se indica en

http://static.springsource.org/spring-webflow/docs/2.3.x/reference/htmlsingle/spring-webflow-reference.html#el-portability
 
También deberemos asegurarnos, como se indica en el enlace anterior, que las variables no devuelvan valores nulos, ya que si no, se puede producir un NullPointerException. En este caso concreto, es importante utilizar la nueva nomenclatura de la forma

`objeto?.propiedad``

que es equivalente a

`objeto==null?null:objeto.propiedad``

Nunca debemos llamar a la propiedad de un objeto si existe la posibilidad de que el objeto sea nulo. Si no hacemos esta comprobación, obtendremos errores del tipo:

org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 20): Field or property 'propertyName' cannot be found on null

Consideraciones en Eclipse

Es muy probable que en Eclipse, al realizar las modificaciones hasta ahora indicadas, se produzca el error

EL Validator cannot run on project because Validation Builder precedes JBoss Knowledge Base Builder

Para arreglar este error debemos ir a la ventana de propiedades del proyecto y ahí seleccionar Builders. Debemos poner el Builder JBoss Knowledge Base Builder por delante del Builder Validation.

Este error se ha detectado concretamente en Eclipse 3.6-SR2 (Helios) y en Eclipse 3.7-SR1 (Indigo).

Modificaciones en páginas y plantillas .xhtml

Deberemos hacer cambios generales en nuestras páginas y plantillas debido a cambios en los tags de JSF2, en general, y a cambios, bastantes, en RichFaces.

Cambios generales

En primer lugar, se debe realizar la sustitución de

`xmlns:c="http://java.sun.com/jstl/core"``

por

`xmlns:c="http://java.sun.com/jsp/jstl/core"``

Ahora, en JSF2 las etiquetas pasan a formar también parte de los componentes JSF. Si no realizamos este cambio, obtendremos errores del tipo

One or more resources have the target of 'head', but no 'head' component has been defined within the view

o, en español,

Uno o más recursos tienen el destino de 'head', pero no se ha definido ningún componente de 'head' dentro de la vista

Cambios debidos a PrimeFaces

El único cambio notable debido a PrimeFaces es que se ha simplificado la declaración de componentes eliminando algunas fichas aque antes eran necesarias. En la versión JSF 1.2 había que declarar en el xhtml, por ejemplo:

<html xmlns=”http://www.w3c.org/1999/xhtml”	
xmlns:f=”http://java.sun.com/jsf/core”
xmlns:h=”http://java.sun.com/jsf/html”
xmlns:p=”http://primefaces.prime.com.tr/ui”>
<head>
<p:resources />
</head>
<body>
<p:editor />
</body>
</html>

Mientras que ahora ya no es necesaria la ficha <p:resources />

<html xmlns=”http://www.w3c.org/1999/xhtml”	
xmlns:f=”http://java.sun.com/jsf/core”
xmlns:h=”http://java.sun.com/jsf/html”
xmlns:p=”http://primefaces.prime.com.tr/ui”>
<head>
</head>
<body>
<p:editor />
</body>
</html>

Como veremos en el siguiente punto, sustituiremos componentes de RichFaces, que dejan de existir, por componentes de PrimeFaces.

Cambios debidos a RichFaces

El cambio de la versión 3.3.3.Final de RichFaces a la versión 4.0.0 conlleva muchísimas modificaciones. Se han realizado muchos cambios en la parte de ajax, a4j, con el fin de adecuarlo a los nuevos estándares de JSF2, que a su vez incluyen ajax. También se han realizado cambios en los nombres de los componentes: algunos se han agrupado en otros, otros han cambiado de nombre (principalmente, cambios en las mayúsculas y minúsculas) y otros, bastantes, han desaparecido totalmente. Algunos componentes han cambiado el nombre de sus atributos, o ya no los tienen, o incluso, también han cambiado sus facet.

Los cambios que se deben realizar para realizar la migración están explicados en la página

RichFaces Migration Guide. 3.3.x - 4.x Migration

Es aconsejable también consultar los foros.

En este epígrafe de este post se van a explicar los cambios más notables que se deben tener en cuenta, así como qué soluciones se deben adoptar en algunos de ellos.

Componentes rich:spacer y rich:separator

Estos dos componentes han desaparecido complementamente, y en el momento de escribir este post, el equipo de RichFaces indica que no tienen intención de recuperarlos. Esto queda registrado en

http://community.jboss.org/thread/164879

y se proponen soluciones en

http://community.jboss.org/wiki/SpacerImplementationForJSF2OrRichFaces4

En nuestro caso, la solución que adoptamos es utilizar los mismos componentes que sí existen en PrimeFaces 2.2. Por tanto, sustituiremos los rich:spacer por p:spacer y los rich:separator por p:separator.

Componente rich:hotKey

Este componente ha sido eliminado. En nuestro caso lo sustituiremos por el equivalente de PrimeFaces, p:hotkey, junto con sus correspondientes atributos.

Componente rich:tab

En este componente debemos sustuir el atributo label por header.

Componente rich:toolBar

Este componente únicamente cambia de nombre: ahora es rich:toolbar.

Se comprueba que en los menús que tengan más de un submenú, se producen errores de javascript que impiden mostrar los submenús, cuando un submenú ya se ha mostrado. Por ello, se recomienda realizar las siguientes sustituciones a los componentes equivalente de PrimeFaces:

  • Sustituir rich:toolbar por p:menubar
  • Sustituir rich:menuitem por p:menuitem ajax='false'
  • Sustituir rich:dropDowMenu por p:submenu label='xxx'. En el label asignaremos el valor indicado en el facet correspondiente, que ahora debe ser eliminado.
  • Sustituir rich:menuGroup por p:submenu.
  • Sustituir rich:menuSeparator por p:separator.

Componente rich:datascroller

Este componente únicamente cambia de nombre: ahora es rich:dataScroller.

Componente rich:dataList

Este componente se fusiona dentro del nuevo componente rich:list. Una solución rápida es sustuituir rich:dataList por rich:list.

Eventos

El atributo event, que aparece en bastantes componentes, por compatibilidad con JSF2, ya no utiliza el prefijo on en el nombre de los eventos. Así pues, tendremos que hacer una sustitución del tipo event="on por event=" en todas nuestras páginas y plantillas xhtml.

Atributo reRender

El atributo reRender ahora se denomina render.

Componente rich:subTable

Este componente ha desaparecido y se ha creado un nuevo componente más complejo denominado rich:collapsibleSubTable.

La fichas rich:subTable deben ser sustituidas por collapsibleSubTable.

Componente rich:simpleTogglePanel

Este componente ha sido sustituido por rich:collapsiblePanel. Además, en este componente se deben sustituir el atributo opened por expanded, y el atributo label por header.

Otra característica importante de este nuevo componente es que ahora pueden mostrar imágenes en la barra de título tanto en la parte derecha, como en la izquierda, cuando, hasta ahora, sólo mostraba imágenes en la parte derecha. Para indicar las imágenes que queremos mostrar se han añadido los atributos leftCollapsedIcon, leftExpandedIcon, rightCollapsedIcon y rightExpandedIcon. A los últimos dos atributos asignaremos la imagen que tuviéramos en los facet closeMarker y openMarker, respectivamente. Desaparecen, por tanto los facet closeMarker y openMarker, ya que han sido sustituidos por los atributos mencionados.

Existen una serie de iconos estándar con un nombre predeterminado que se pueden indicar estos atributos. Los nombres que se pueden usar son: disc, grid, chevron, triangle, chevronUp,chevronDown, none, y transparent. none indica que no se mostrará ningún icono. Por tanto, en una primera conversión, dado que el componente rich:simpleTogglePanel no mostraba iconos en la parte de la izquierda, lo lógico es asignar el valor none a los atributos leftCollapsedIcon y leftExpandedIcon.

Componente rich:toolTip

Este componente cambia de nombre: ahora es rich:tooltip (la segunda t ahora en minúsculas). También el atributo for pasa a llamarse target.

Conviene que el componente rich:tooltip esté dentro del componente al que hace referencia, y que el texto que se quiere presentar esté entre y , porque a veces el atributo value puede no funcionar como se espera.

Componente rich:componentControl

En este componente ha sido sustituido el atributo for por el de target. Ha desaparecido el atributo attachTo, siendo ahora necesario embeber o anidar este componente dentro del componente con el que está relacionado, anteriormente indicado en el atributo attachTo.

Componente rich:modalPanel

Este componente ha sido sustituido por el nuevo componente popupPanel, que ahora, con el atributo modal, puede ser o no modal.

Tendremos que reemplazar los <rich:modalPanel con y los  con . Además, el atributo showWhenRendered de este componente ahora es show en el nuevo componente popupPanel.

Puede ser conveniente utilizar el nuevo atributo onmaskclick con el fin de que cuando el usuario haga click fuera del panel modal, éste se cierre. Para ello deberemos asignar este nuevo atributo  del siguiente modo:

onmaskclick="#{rich:component('idpopuppanel')}.hide()"

Componente rich:paint2D

Este componente ha sido eliminado. Aunque supuestamente JSF2 proporciona recursos para sustituirlo, una forma posible de sustitución sea utilizando a4j:mediaOutput. Por ejemplo, un código típico dentro de xhtml del rich:paint2D

<rich:paint2D id="image"  
width="#{bean.imageWidth}"
height="#{bean.imageHeight}"
format="jpeg"
paint="#{bean.paintImage}"
data="#{bean.randomString}" />

podría ser sustituido con

<a4j:mediaOutput id="image" element="img" mimeType="image/jpeg"  
createContent="#{bean.paintImage}" value="#{bean.randomString}"
style="width:200px; height:100px;" cacheable="false">

En la parte del servidor, el típico método donde se construye la imagen para el componente rich:paint2D

public void paintImage(Graphics2D g2d, Object obj) {  
BufferedImage image = generateImage();
try {
g2d.setClip(0, 0, image.getWidth(), image.getHeight());
g2d.drawImage(image, 0, 0, null);
} catch (Exception ex) {
System.err.print(ex.getMessage());
}
}

podría ser sustituido con

public void paintImage(OutputStream stream, Object object) throws IOException {  
BufferedImage image = generateImage();
ImageIO.write(image, "jpeg", stream);
stream.close();
}

Siendo ImageIO una utilidad del paquete javax.imageio.

Componente rich:comboBox

Este componente ha sido eliminado, aunque puede ser sustituido en ciertos casos por el nuevo componente autocomplete, muchas veces podrá ser sustituido de una forma más directa por el componente rich:select. Al ser sustituido por este componente deberemos indicar enableManualInput="true", sustituir listStyle por listClass y eliminar los atributos margin-top y with. El atributo suggestionValues, por ejemplo, de la forma suggestionValues="#{bean.suggestionList}" debe ser sustituido por un elemento hijo dentro de rich:select.

Componente a4j:support

Este componente ha sido sustituido por a4j:ajax con el fin de seguir mejor los estándares de JSF2. Sin embargo, hay que tener en cuenta que ya no se da soporte a atributos como requestDelay, requestGroupingId e ignoreDupResponse, que ahora deben ser indicados exclusivamente mediante un componente anidado a4j:attachQueue.

Otro atributo que desaparece es action. Si nos hace falta un action deberemos apoyarnos en el componente a4j:jsFunction.  Por ejemplo, para resolver el problema de actualizar uno o varios componentes dentro de la misma ventana, al seleccionar un elemente de un combo, que hasta ahora se resolvía con un código similar al siguiente:

<h:selectOneMenu id="mySelect"  
value="#{bean.selectValue}" styleClass="medium">
<f:selectItems value="#{bean.listOfValues}" />
<a4j:support id="changeListener" action="changeAction"
event="onchange" reRender="otherComponent"/>
</h:selectOneMenu>

ahora debemos utilizar el siguiente código:

<a4j:jsFunction name="changeTransitionTrigger" action="changeAction" render="otherComponent" />  
<h:selectOneMenu id="mySelect"
value="#{bean.selectValue}" styleClass="medium"
onchange="changeTransitionTrigger()">
<f:selectItems value="#{bean.listOfValues}" />
</h:selectOneMenu>

En la solución propuesta se ejecuta la action changeAction antes de que en el servidor se actualice el valor del combo. Si queremos que se ejecute después de haberse actualizado el valor en el servidor, utilizaremos el siguiente código:

<a4j:jsFunction name="changeTransitionTrigger" action="changeAction" render="otherComponent" />  
<h:selectOneMenu id="mySelect"
value="#{bean.selectValue}" styleClass="medium"
onchange="changeTransitionTrigger()">
<f:selectItems value="#{bean.listOfValues}" />
</h:selectOneMenu>

Componente a4j:status

Este componente no ha cambiado, pero si se utiliza una imágene para mostrar que el sistema en progreso, por ejemplo, con el código:

<a4j:status>  
<f:facet name="start">
<h:graphicImage value="/images/ajax/ajax_process.gif"/>
</f:facet>
</a4j:status>

normalmente, a partir de la tercera vez que se llama a un proceso ajax y se necesite mostrar la imagen de espera, se pueden producir errores del tipo

java.lang.IllegalStateException: CDATA tags may not nest
at com.sun.faces.renderkit.html\_basic.HtmlResponseWriter.startCDATA(HtmlResponseWriter.java:626)

y

java.lang.IllegalStateException: El ID del componente form\_general:j\_id65 ya se ha encontrado en la vista.  

o

java.lang.IllegalStateException: Component ID form\_general:j\_id65 has already been found in the view.

El elemento repetido siempre corresponde al elemento relacionado con la imagen del componente status. Este comportamiento se da, principalmente, en páginas donde se carguen/creen componentes de forma dinámica y no estén inicialmente definidos en el propio xhtml. Conviene, por tanto, no utilizar los facet start y complete, y en su lugar utilizar los atributos startText y stopText.

Componente rich:fileUpload

Este componente no funciona correctamente con Spring SWF, ni en Spring MVC. Produce errores del tipo

java.lang.ClassCastException: java.util.ArrayList cannot be cast to [Ljava.lang.String;

También se obtienen errores indicados en la incidencia no resuelta

Fileupload doesn't work in Richfaces 4.0.0 Final [SWF-1482]

Por tanto, se aconseja sustituir este componente por el equivalente de PrimeFaces p:fileUpload.

El componete de PrimeFaces, versión 2.2.1, sí que permite el auto upload, esto es, que cuando el usuario seleccione el archivo, éste inmediatemente suba al servidor, mientras que el componete de RichFaces, version 4.0.0.Final no lo permite.

Actualización a Spring 3.1.0, RichFaces 4.1.0 Final y PrimeFaces 3.0

Se ha comprobado que todo lo citado hasta ahora, sigue vigente para las versiones de Spring 3.1.0, RichFace 4.1.0 y PrimeFaces 3.0.

Configuración de acceso a base de datos

En Spring 3.1 ya no existe el archivo persistence.xml, que usualmente está bajo META-INF. Se considera que este archivo normalmente tiene siempre los mismos valores, y pocas veces diferente a los valores por defecto. Por esta razón, los parámetros que se indican en este archivo, han pasado a ser parámetros del objeto instancia de la clase org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean. Se ha añadido el parámetro packagesToScan donde indicaremos el nombre del paquete donde están nuestras clases modelo, esto es, la clases que mapean las tablas de la base de datos. También se ha añadido el parámetro jpaPropertyMap que básicamente es un mapa con los parámetros que se indicaban en el persistence.xml. Un ejemplo, de este objeto, que por norma general se indica en el archivo data-access-config.xml sería el siguiente:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">  
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.mycompany.mymodel"/>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<!-- <entry key="hibernate.hbm2ddl.auto" value="create-drop" /> -->
<!-- <entry key="hibernate.hbm2ddl.auto" value="update" /> -->
<entry key="hibernate.show_sql" value="false"/>
<!--
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/>
-->
<entry key="hibernate.cache.use_second_level_cache" value="true"/>
<entry key="hibernate.cache.use_query_cache" value="true"/>
<entry key="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory"/>
<entry key="hibernate.generate_statistics" value="true"/>
<entry key="hibernate.cache.provider_configuration_file_resource_path" value="ehcache.xml"/>
</map>
</property>
<bean>

PrimeFaces 3.0

En los archivos .xhtml tenemos que realizar el siguiente cambio en la cabecera:

xmlns:p="http://primefaces.prime.com.tr/ui"

sustituir por

xmlns:p="http://primefaces.org/ui