Monday, January 16, 2012

Seam 3 on Weblogic 12c

The JSR299/CDI(context-dependency-injection) is now part of any JavaEE6 application server, and I have had been exploring it on one such app-server Weblogic 12c, which bundles Weld 1.1.3.SP1 as the CDI container.

In JSF world, they say, it is a good practice to use appropriate scope on JSF managed-beans to make efficient use of the server-memory, and that the same applies to CDI managed-beans. For CDI beans, the supported scopes are @RequestScoped, @SessionScoped, @ApplicationScoped, and @ConversationScoped. However, there is no support in CDI for a JSF equivalent @ViewScoped bean, which happens to be the best choice in certain use cases.

So what do we do if we want @ViewScoped added to CDI managed-beans? The answer in my search so far is: cdi-extensions. The seam-faces module from JBoss is one such CDI extension that adds the @ViewScoped support. One simply needs to add the seam-faces module jars to the web or enterprise archive and the @ViewScoped is added automagically.

The auto magic part is that the seam3 modules are BDAs(bean-deployment-archives), jars that contain META-INF/beans.xml file, and therefore they are scanned for beans in the CDI bootstrap. The seam-faces module has dependencies on other seam-modules such as seam-international and those jars are also needed. A recommendation if you are using maven is to add a dependency management to seam-BOM(bill-of-material) and you would get all the needed dependencies, tested version jars etc. That's it per the JBoss documentation on getting the seam-faces working.

--snip--
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <seam.version>3.0.0.Final</seam.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.seam</groupId>
                <artifactId>seam-bom</artifactId>
                <version>${seam.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies> 
        <dependency>
            <groupId>org.jboss.seam.faces</groupId>
            <artifactId>seam-faces</artifactId>
        </dependency>
       ........
    </dependencies>
--snip--

In reality however, getting seam-faces to work on Weblogic 12c wasn't an easy experience. It failed with a number of ClassNotFound Exceptions despite using the BOM dependency. It seemed to presume the app-server to contain some seam-classes. That wasn't the case for weblogic, and adding explicit dependencies to the pom helped get past this issue.

Next it failed due to unsatisfied CDI dependencies with exception as follows:

--snip--
Caused By: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Messages] with qualifiers [@Default] at injection point [[parameter 2] of [method] org.jboss.seam.faces.status.MessagesAdapter.convert(PhaseEvent, Messages)]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:258)
at org.jboss.weld.bootstrap.Validator.validateObserverMethods(Validator.java:484)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:314)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:361)
at com.oracle.injection.provider.weld.WeldInjectionContainer.start(WeldInjectionContainer.java:105)
--snip--

Above exception indicates a problem satisfying an injection-point inside MessagesAdapter bean inside seam-faces. It seems to inject "Messages" bean into MessagesAdapter and fails to find the "Messages" bean. Interestingly, the "Messages" bean is defined inside seam-international module and that was part of the deployment-archive, but still it didn't find the bean. So after some deliberation and a separate test, I concluded that there is a problem/bug in Weblogic 12c that cyclic dependencies across BDAs are not satisfied. 

In other words, if you have two BDAs as part of a  test.war: a.jar and b.jar, added in WEB-INF/lib, and then trying to inject a bean from a.jar into a class in b.jar doesn't work. This is needed per the JSR 299 specification. However, there is no problem in resolving beans inside the same BDA. So what's a workaround? Merge the two jars: a.jar and b.jar into a single jar and add that one to test.war.

Here is a way to create an uber jar of seam-faces and seam-international using maven:

--snip--
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.suhas.commonjar</groupId>
        <artifactId>seamfaces-n-seami18n</artifactId>
        <packaging>jar</packaging>
        <version>1.0-SNAPSHOT</version>
        <name>seamfaces-n-seami18n</name>
        <url>http://maven.apache.org</url>
        <dependencies>
            <dependency>
                <groupId>org.jboss.seam.faces</groupId>
                <artifactId>seam-faces</artifactId>
                <version>3.0.0.Final</version>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam.international</groupId>
                <artifactId>seam-international</artifactId>
                <version>3.0.0.Final</version>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam.solder</groupId>
                <artifactId>seam-solder</artifactId>
                <version>3.0.0.Final</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <build>
                <plugins>
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-shade-plugin</artifactId>
                                <executions>
                                        <execution>
                                                <phase>package</phase>
                                                <goals><goal>shade</goal></goals>
                                        </execution>
                                </executions>
                                <configuration>
                                        <finalName>uber-${artifactId}-${version}</finalName>
                                </configuration>
                        </plugin>
                </plugins>
        </build>
</project>
--snip--

With above uber jar in the deployment-archive instead of seam-faces and seam-international helped resolve the CDI unsatisfied-dependencies problem, and now we have seam-faces working on 12c.

In summary, if you run into unsatisfied-dependencies problem on Weblogic 12c across two library BDAs, then see if creating an uber jar workaround may be be acceptable.

1 comment:

  1. Hello! Great post, thanks! But, i can't retry your path :( May be you sharing your example war, what good deploy to weblogic 12c. Thank you! :)

    ReplyDelete