Por defecto WebLongi 10.3.4 viene con JSF 1.2 (que no soporta anotaciones para inyección de dependencias, etc – i.e. vale hongo). Sin embargo, supuestamente ésta es la primera versión que puede soportar JSF 2, junto con otros features de JEE6 como vimos haciendo el proyecto base >>.
instalación
Después de harto luchar tratando de encontrar una versión de JSF en maven que pudiera deployar junto con mi aplicación a cada servidor – con lo que se tiraba un lindo
java.lang.UnsupportedOperationException at javax.faces.application.Application.getResourceHandler(Application.java:286) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:307) at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227) ...
en tiempo de ejecución – pude aceptar que si queremos usar JSF 2, tenemos que usar la versión proveída por WebLogic.
La susodicha se encuentra en wlserver_10.3/common/deployable-libraries y responde al nombre de jsf-2.0.war (si es que quiere).
Para deployarlo, los buenos de Oracle nos ofrecen dos maneras >>:
- Usando el comando de consola weblogic.Deployer, lo cual, adivine ud., no funciona… bueno, funciona si le pasamos la ruta a weblogic.jar – pero éso no lo dice la documentación.
java -cp ~/ruta/a/weblogic.war weblogic.Deployer -adminurl t3://localhost:7001 -user weblogic -password welcome1 -deploy -library ~/ruta/a/WL/common/deployable-libraries/jsf-2.0.war
- Desde la consola de administración (más fácil, pero muy manual para ser serio) en Dependencias > Instalar:
Pero éso no es todo amigos. Además hay que decirle al servidor que esta app usa esa versión de JSF. Ésto se hace creando un archivo WEB-INF/weblogic.xml
<?xml version="1.0" encoding="UTF-8"?> <wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" 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 http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd"> <wls:weblogic-version>10.3.4</wls:weblogic-version> <wls:library-ref> <wls:library-name>jsf</wls:library-name> <wls:specification-version>2.0</wls:specification-version> <wls:implementation-version>1.0.0.0_2-0-2</wls:implementation-version><!--ojo--> <wls:exact-match>true</wls:exact-match> </wls:library-ref> </wls:weblogic-web-app>
Éso es lo particular.
Como en todo .WAR, también hay que crear el WEB-INF/web.xml donde le especificamos que el Servlet de JSF tiene que interpretar los archivos .xhtml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> </web-app>
y, bueno, darle la dependencia de jsf 2 a maven para que pueda compilar javax.faces.bean… en el pom.xml
<dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.0</version> <scope>provided</scope><!--ojo--> </dependency>
ojo que tiene scope provided justamente porque usamos el de WLS, no queremos que empaquete uno.
Con éso podemos conectar una vista con un ManagedBean, por ejemplo:
<h:outputText value="#{miBean.prop}">
@ManagedBean public class MiBean{ public string prop; }
inyección
En teoría, ésto también debiera permitir inyectar EJBs en el ManagedBean (según la sintaxis de EJB 3)
@Remote public interface EJB{ public void metodo(); } @Stateless public class EJBBean implements EJB{ public void metodo(){} }
así
@ManagedBean public class MiBean{ @EJB EJB elEjb; //... }
pero ésto no funciona.
Siempre queda null.
En teoría, ésto debiera pasar justamente si no usamos la implementación de JSF de WebLogic >> ya que ésta trae un JAR (propietario) wls.jsf.di.jar que trae una única clase com.sun.faces.spi.InjectionProvider que es la encargada de efectuar la inyección de dependencia.
Intenté también aislando este jar y poniéndolo en el WEB-INF/lib… pero nada.
¿Habrá algo que estoy haciendo mal?
En efecto, si miro dentro del WAR tengo jsf-api en WEB-INF/lib…
Ésto porque me falta hacer un mvn clean package para que tome el scope provided de jsf-api. Con ésto ya no está la librería y parece que ya estamos usando JSF 2 ya que al deployar el servidor tira este error
JavaServer Faces 2.0 requires Dynamic Web Module 2.5 or newer
Para solucionar ésto, hay que hacer un web.xml conforme a la versión de Servlet 2.5 en vez de 3.0 >>
<?xml version="1.0" encoding="UTF-8"?> <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"> <!--servlet, como arriba --> </web-app>
Parece que ahora sí está haciendo la inyección de dependencia pues al deployar se cae con
java.lang.ClassNotFoundException: com.numerica.test.interfaces.EJB weblogic.utils.classloaders.GenericClassLoader.findLocalClass(GenericClassLoader.java:297) at weblogic.utils.classloaders.GenericClassLoader.findClass(GenericClassLoader.java:270) at weblogic.utils.classloaders.ChangeAwareClassLoader.findClass(ChangeAwareClassLoader.java:64) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at java.lang.ClassLoader.loadClass(ClassLoader.java:248) Truncated. see log file for complete stacktrace
Ésto significa que no está pudiendo inyectar en ejb remoto porque no tiene ni la librería en WEB-INF/lib ni su declaración en el MANIFEST.MF >>.
Como quiero que el JAR corra por separado, necesito lo segundo, para lo cual puedo usar el maven-war-plugin >>
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin>
entonces, si declaro la dependencia al jar como opcional
<groupId>com.numerica.test</groupId> <artifactId>miJAR</artifactId> <version>1.0</version> <type>ejb</type> <optional>true</optional><!--ojo--> </dependency>
queda en el MANIFEST.MF
Manifest-Version: 1.0 Built-By: numerico Build-Jdk: 1.6.0_24 Class-Path: miJAR-1.0.jar Created-By: Apache Maven 3.0.4 Archiver-Version: Plexus Archiver .
pero sigue el mismo error.
En el fondo el asunto es que, pese a que al compilar sí tengo la interfaz disponible – y por éso el compilador no se cae en el import – probablemente la clase que efectúa la inyección de la dependencia la necesita en tiempo de deploy.
Entonces el jar (o parte de él) sí tiene que estar disponible en WEB-INF/lib.
Aquí es donde nos va a ser útil el maven-ejb-plugin. Éste puede generar un jar cliente (que tiene sólo las interfaces) >> :
En el pom.xml del JAR
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-ejb-plugin</artifactId> <version>2.3</version> <configuration> <generateClient>true</generateClient><!--false por defecto--> </configuration> </plugin>
El cual luego referenciamos desde el del WAR como ejb-client
<dependency> <groupId>com.numerica.test</groupId> <artifactId>MiJAR</artifactId> <version>1.0-SNAPSHOT</version> <type>ejb-client</type><!--ojo--> </dependency>
y ahora sí funciona.
Es decir, intenta hacer la inyección de la interfaz, pero no encuentra el bean
[J2EE:160200]Error resolving ejb-ref 'com.numerica.test.MiBean/EJB' from module 'MiWAR' of application '_auto_generated_ear_'. The ejb-ref does not have an ejb-link and the JNDI name of the target bean has not been specified. Attempts to automatically link the ejb-ref to its target bean failed because no EJBs in the application were found to implement the 'com.numerica.test.interfaces.EJB' interface. Please link or map this ejb-ref to its target EJB and ensure the interfaces declared in the ejb-ref are correct. at weblogic.deployment.BaseEnvironmentBuilder.autowireEJBRef(BaseEnvironmentBuilder.java:427)
Ésto es porque, como es un ejb remoto, hay que especificarle el nombre JNDI del bean para que lo encuentre.
En EJB 3 no hay una manera portable de hacerlo >> – cada contenedor tiene la suya – y la de WebLogic es con la propiedad mappedName de la anotación @EJB
@EJB(mappedName="nombre") private EJB ejb;
Éste debe coincidir con el del EJB, el cual también podemos especificar en la anotación @Stateless del EJB referenciado
@Stateless(mappedName="nombre")
y ahí tenemos la bendita inyección de dependencias funcionando…
…