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"]
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:
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