Thursday, May 01, 2008

EJB3 Seam GWT - part II, GWT 1.5m2

Short blog --
Working with Seam 2.1-SNAPSHOT and GWT 1.5M2 (with EJb3/jboss 4.2 backend).

To start off with a bang - JPA (hibernate) annotated beans are working!!! Yes, you can now use annotated entity beans as regular POJO's with Seam and GWT.

p.s. I really do *not* like the annotation approach and preferred the orm.xml deployment descriptor approach to keep the POJO truly clean and free from decoration with annotation behaviours -- but there are some problems with the hibernate orm.xml implementation that they 'lack resources' to correct, so whatever; use the actually supported annotation approach.

I unfortunately still not sure how to post code to blogger (need a full project, just sharing snippets is insufficient), but before you start jumping in too far here are the caveats:
  • Long and Date fields do not work as of 5/1/2008. GWT changed the behaviour of Longs in Milestone 2 of GWT 1.5, and the Seam guys haven't modified to support this yet -- it is, afterall, still a milestone release of GWT. http://jira.jboss.org/jira/browse/JBSEAM-2935
  • Arrays do not work as of 5/1/2008. This confuses me - Seam 2.0.0.GA and GWT 1.4.60 arrays worked fine, not sure why this is now broken. http://jira.jboss.org/jira/browse/JBSEAM-2933
  • Lists (genericized) now do work, and can be used as an alternative to the broken array implementation.
  • You can implement entitybean/pojo/DTO's that use the Long and Date fields, but when you RPC those fields you will get errors. You can test with your DTO and List as long as you do not actually use Long and Date fields (leave them blank/null).

Someone reminded me about something I've taken for granted - no, I am not using JNDI lookups. This is direct, session bean with Seam annotation exposed directly with the Seam-GWT remoting implementation to the GWT client. No middle JSF layer or JNDI layer or tomcat/servlet layer -- GWT web talking to EJB3/Seam server.

Still working on a more full example, have fun till then!

4 comments:

Kevin Corby said...

I accidentally posted this on your older blog entry about GWT 1.4.61, but this entry is probably more appropriate.
-----------------------
Hi Darren,

I am working on an application that is using GWT for the front end and EJBs with Seam web remoting for the back end. Your sample app from the last blog post was helpful, thanks. I have simple security set up using the Seam @Restrict annotation. When my restricted method gets called before login, the server throws a NotLoggedInException as expected. However, this exception does not translate to the GWT side, where it becomes just a generic invocationexception. Have you had an experience with this and can you suggest a way to pass the exception through, especially given that it originates from Seam code and doesn't currently pass through any of mine?

Thanks,
Kevin

dhartford said...

Hi Kevin,
Sorry for the delayed response, been quite busy.

I have run into the NotLoggedInException and also am frustrated, no solution has been found yet.

As far as feeding the exception back to the client (although it probably wont help much for now), create your own serialized (isSerializable) exception to re-throw if you catch the NotLoggedInException.

Kevin Corby said...

Hi Darren,

Thanks for the response. I did eventually find a somewhat satisfactory solution for this problem.

First I tried adding a Seam interceptor to catch the NotLoggedInException and translate it to something the client could deal with, but had no luck getting an interceptor to run around the Seam interceptors. Since my interceptors always ran inside of the Seam interceptors they only got called only once the authorization was passed and were of no use for this.

So instead I implemented an interceptor using aspectj and weaved it around the Seam code. I am using Maven to build and had to put my solution in an additional project that looks like this:

./pom.xml
./src
./src/main
./src/main/java
./src/main/java/com
./src/main/java/com/ccri
./src/main/java/com/ccri/gotm
./src/main/java/com/ccri/gotm/auth
./src/main/java/com/ccri/gotm/auth/aspects
./src/main/java/com/ccri/gotm/auth/aspects/TranslateSecurityExceptionsAspect.aj
./src/test
./src/test/java
./src/test/java/com
./src/test/java/com/ccri
./src/test/java/com/ccri/gotm

TranslateSecurityExceptionsAspect.aj:
package com.ccri.gotm.auth.aspects;

import org.jboss.seam.security.SecurityInterceptor;
import org.jboss.seam.intercept.InvocationContext;

public aspect TranslateSecurityExceptionsAspect {

pointcut securityIntercept():
execution(Object SecurityInterceptor.aroundInvoke(InvocationContext));

after() throwing(org.jboss.seam.security.NotLoggedInException notLoggedInException)
throws com.ccri.gotm.auth.exceptions.NotLoggedInException: securityIntercept() {
throw new com.ccri.gotm.auth.exceptions.NotLoggedInException();
}
}

pom.xml:
<lt;?xml version="1.0"?>
<lt;project>
<lt;parent>
<lt;artifactId>gotm<lt;/artifactId>
<lt;groupId>com.ccri<lt;/groupId>
<lt;version>1.0-SNAPSHOT<lt;/version>
<lt;/parent>
<lt;modelVersion>4.0.0<lt;/modelVersion>
<lt;groupId>com.ccri.gotm<lt;/groupId>
<lt;artifactId>gotm-aspectj<lt;/artifactId>
<lt;name>gotm-aspectj<lt;/name>
<lt;version>1.0-SNAPSHOT<lt;/version>
<lt;url>http://felix.ccri.com/gotm-site/gotm-aspectj/<lt;/url>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<complianceLevel>1.5</complianceLevel>
<outxml>true</outxml>
<weaveDependencies>
<weaveDependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
</weaveDependency>
</weaveDependencies>
<includes>
<include>**/*.aj</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>gotm-jar</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
<version>${seam.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

Then in the ear I had to include the jar created by this project, as well as the aspectjrt jar and the jboss-seam jar (the latter with type = ejb):
<dependency>
<groupId>org.jboss.seam</groupId>
<artifactId>jboss-seam</artifactId>
<version>${seam.version}</version>
<type>ejb</type>
</dependency>

Overall though, my experience has been that there are currently too many gotchas like this that make Seam + GWT not a worthwhile approach. If I had it to do again I might still use Seam but would skip GWT and just use JSF. I don't think the benefits of GWT outweight the additional complexity and compilation time.

dhartford said...

I'm still keeping a distant eye on Seam, but I've actually gone the other way and kept GWT, but removed Seam as the server-interface layer and just writing wrapper servlets as a thin layer between server code (whether pojo or EJB) and the GWT RPC mechanisms.

Webbeans still hasn't really blossomed as I'd hope for GWT support -- all GWT needs is a Stateful conversatation support on the EJB layer and be done.