Showing posts with label jboss seam. Show all posts
Showing posts with label jboss seam. Show all posts

Monday, April 4, 2011

A CDI Extension for Query Generation

While the DAO pattern has come out of fashion with Java EE 6, it can still be a useful approach to centralize query related logic when you have to do more than just delegating to the entity manager. Often this approach leads then to small frameworks containing reusable code within a project, or also sometimes into a bigger framework spreading over complete IT departments within a company.

Inspired by features of Grails or the Spring Data JPA project, my plan was to learn more about CDI extensions by implementing a proof of concept on such a DAO framework based on CDI. CDI is part of Java EE 6 and, already by itself a powerful addition to the platform programming model, provides SPIs to extend it even further. A prominent sample is the Seam framework, which in its latest version contains a lot of those extensions. Just drop them in your classpath and they are ready for use. Impressive enough to learn more about the technology.

While I was getting my hands dirty it seemed to me the result is useful enough to share it here - and also of course to demonstrate the power and easiness of creating CDI extensions. In this article I’ll give you a quick start on CDI extensions as well as (hopefully) an idea on how a portable framework might look like based on this technology. All code presented here is available on GitHub.

The DAO Framework

Some common ingredients of a DAO framework are captured in the code snippet below:

@Dao
public interface SimpleDao extends EntityDao<Simple, Long> {

Simple findByNameAndEnabled(String name, Boolean enabled);

@Query(named=Simple.BY_NAME)
List<Simple> findByNamedQuery(String name);

@Query(named=Simple.BY_NAME)
List<Simple> findByNamedQueryRestricted(String name,
@MaxResults int max, @FirstResult int first);

@Query(named=Simple.BY_ID)
Simple findByNamedQueryNamedParams(
@QueryParam("id") Long id,
@QueryParam("enabled") Boolean enabled);

@Query("select s from Simple s where s.name = ?1")
Simple findByQueryString(String name);

}

Typically a DAO framework has a common interface concrete DAOs can extend from. Using generics here allows to have a standard set of methods like saving or retrieving all entities of a specific type, and can also be used during query generation. Usually this is nothing that cannot be done easily with an entity manager. But once you have injected a DAO in your service class - do you really want to inject the entity manager as well? In order to keep code leaner, a DAO base interface should provide this kind of methods and of course implement them all automagically - nothing you would like to rewrite again and again.

Some other features are shown in the method declarations above. Automatic query generation out of method names and parameters as GORM method expressions do, or creating queries based on annotation meta data and parameters will often allow just leaving those easy cases to the query generator and keep the code lean to focus on the complex ones.

The CDI Approach

One way to implement such a framework is over a CDI extension. CDI allows extensions to listen to various lifecycle events:
- Before CDI starts discovering beans.
- While it processes annotated types, injection targets, producers, beans and observers.
- And when it finishes with both discovery and validation.

As in our case we are dealing with plain interfaces, the easiest approach is to simply annotate the interface and then listen for annotation processing events. The sample above shows a Dao annotation on the interface, but this would be placed on the extended AbstractEntityDao interface so developers won’t have to worry about it.

So on the extension we listen for annotated types, check if it is our Dao annotation and register a proxy bean which implements the annotated type. Registering the extension a matter of two things:
1. Implementing the extension class and listen for the Dao annotation.

public class QueryExtension implements Extension {

<X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> event, BeanManager beanManager) {
// all the required information on the type is found in the event
// the bean manager is used to register the proxy
}

}

2. Register the extension class as a service provider in the appropriate file (META-INF/services/javax.enterprise.inject.spi.Extension).

Registering the proxy with the bean manager is slightly more work, but luckily someone has already done that. If you work with CDI extensions, make sure to include Seam Solder - the Swiss army knife for CDI developers. Solder has built-in support for so called service handlers, where you annotate an abstract type with a reference to the handler class. The documentation use case looks probably kind of familiar ;-) All our extension will have to do is to override the handler lookup - and we’re done with registering the proxy! The reason we don't use the ServiceHandlerExtension directly is to separate the handler reference from the annotation, and that we can have a chance to e.g. validate and process further meta data in the extension class.

public class QueryExtension extends ServiceHandlerExtension {

@Override
protected <X> Class<?> getHandlerClass(ProcessAnnotatedType<X> event) {
if (event.getAnnotatedType().isAnnotationPresent(Dao.class)) {
return QueryHandler.class;
}
return null;
}

}

public class QueryHandler {

@Inject
private Instance<EntityManager> entityManager;

@AroundInvoke
public Object handle(InvocationContext ctx) throws Exception {
...
}

}


The handler class simply has to provide a public method annotated with @AroundInvoke, where you get all the required information to build up your query dynamically. Note that in the handler class you will also be able to use CDI services like injection.
As a framework user, all you will have to do is to drop the JAR in the classpath and annotate the interface. Look mom, no XML! Well almost, you still need an (empty) beans.xml somewhere to activate all the CDI magic.

Testing the Extension

Arquillian is a relatively new testing framework which allows you to create dynamic deployment units and run them in a container. A great feature for an extension as you can easily test it live in a unit test without leaving the IDE! This looks like the following:

@RunWith(Arquillian.class)
public class QueryHandlerTest {

@Deployment
public static Archive<?> deployment() {
return ShrinkWrap.create(WebArchive.class, "test.war")
.addClasses(QueryExtension.class)
.addAsWebInfResource("test-persistence.xml", ArchivePaths.create("classes/META-INF/persistence.xml"))
.addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))
.addAsWebInfResource("glassfish-resources.xml")
.addClasses(SimpleDao.class)
.addPackage(Simple.class.getPackage());
}

@Inject
private SimpleDao dao;

@Produces
@PersistenceContext
private EntityManager entityManager;

@Test
public void shouldCreateQueryByMethodName() {
// given
final String name = "testCreateQueryByMethodName";
createSimple(name);

// when
Simple result = dao.findByNameAndEnabled(name, Boolean.TRUE);

// then
Assert.assertNotNull(result);
Assert.assertEquals(name, result.getName());
}

}

This creates a stripped down web archive deployment, in this sample for an embedded GlassFish. The test class itself is registered as a bean in the test and can therefore use injections (CDI or container injections, see the @PersistenceContext). Resources like persistence units can be added on demand as we see in the deployment method (persistence.xml for the persistence unit, glassfish-resources.xml contains a data source definition).

The container gets started and we can see our injected proxy in action. In the sample above we then create some test data and see if our generated query fetches the right data back.

Conclusion

Of course this article is a very simplified version of the whole setup - especially the Arquillian setup required some trial and error as the framework is still in Alpha (if anybody finds out how to create the data source without deploying the glassfish-resources.xml into a web archive - let me know).

Check out the project source on GitHub to get started. The module structure might look a little complicate but it follows common Seam module structure separating API (in our case the client annotations) from implementation (the extension code). Documentation is still "basic" but looking at the unit tests might give an indication on the usage.

Once the project setup is done, things get extremely productive though. Seam Solder provides a rich tool set you can use to create an extension, Arquillian lets you immediately test your code inside a container. The result is an easy to reuse and easy to distribute framework you will probably get back to in many of your following Java EE 6 projects.

Friday, July 30, 2010

Using Seam 2 with JPA 2

It’s a difficult time to architect new Java web applications. Seam 2 is a proven and well working application stack, but we will hardly see many new versions on the Seam 2 train. Java EE 6 is in general an excellent option, but if your customer’s choice of application server does not yet support it, it is not reasonable. Also there is still some time left for Seam 3 prime time, which builds on top of Java EE 6.

Facing this kind of choice recently, I looked into possible migration paths between the two variants. One thing I have seen often on Seam 2 applications is that people really like the Hibernate criteria API and therefore use Hibernate directly. While Hibernate is an excellent ORM framework, it’s preferrable to use the JPA API when moving to Java EE 6. So - why not use Seam 2 with JPA 2, which finally features an even better (typesafe) criteria API?

It turns out to be a quite easy setup (once you get classloading right), with some small extra modifications. I’ve been using Maven, Seam 2.2 and Hibernate 3.5.4 on JBoss 4.2.3. Lets start with preparing the server. You need to remove the old Hibernate and JPA classes and add the new persistence provider with some dependencies (of course this will be different on other servers):

To be removed from server/lib:


Add to server/lib:


Next lets create a sample Seam project. I’m using the CTP Maven archetype for Seam. In the POM file, remove the references to the Hibernate and old JPA libraries and add the new JPA 2 libraries:

<!-- Remove all embedded dependencies
<dependency>
<groupId>org.jboss.seam.embedded</groupId>
<artifactId>...</artifactId>
</dependency -->

<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.0.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.5.4-Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>1.0.0.Final</version>
<scope>provided</scope>
</dependency>

Note: If you’ve been using embedded JBoss for your integration tests, this will probably not work without exchanging the JARS there too. I’ve been moving away from this approach as it turned out to be a common reason for headache on our nightly builds as well as running tests in Eclipse. I’m very excited to see Arquillian evolving on this topic!

JPA 2 integrates with JSR 303 bean validation, which is the successor of Hibernate Validator. Unfortunately Seam 2 has references to Hibernate Validator 3, where JPA needs version 4. Adding the validator legacy JAR fixes this problem. As bean validation is now part of Java EE 6, we can add it to the server classpath as shown above, as well as to our POM:

<dependency>
<artifactId>validation-api</artifactId>
<groupId>javax.validation</groupId>
<version>1.0.0.GA</version>
<scope>provided</scope>
</dependency>

Now it’s time to move to some code. You can jump straight in your persistence config files and bring the schema up to version 2:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0"> ...

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
version="2.0"> ...

Seam proxies entity managers to implement features like EL replacements in JPQL queries. This proxy does not implement methods new in JPA 2 and will therefore fail. You can write your own proxy very easy:

public class Jpa2EntityManagerProxy implements EntityManager {

private EntityManager delegate;

public Jpa2EntityManagerProxy(EntityManager entityManager) {
this.delegate = entityManager;
}

@Override
public Object getDelegate() {
return PersistenceProvider.instance()
.proxyDelegate(delegate.getDelegate());
}

@Override
public void persist(Object entity) {
delegate.persist(entity);
}
...
}

Add the special Seam functionality as needed. In order to use the proxy with Seam, you’ll have to overwrite the HibernatePersistenceProvider Seam component:

@Name("org.jboss.seam.persistence.persistenceProvider")
@Scope(ScopeType.STATELESS)
@BypassInterceptors
// The original component is precedence FRAMEWORK
@Install(precedence = Install.APPLICATION,
classDependencies={"org.hibernate.Session",
"javax.persistence.EntityManager"})
public class HibernateJpa2PersistenceProvider extends HibernatePersistenceProvider {

@Override
public EntityManager proxyEntityManager(EntityManager entityManager) {
return new Jpa2EntityManagerProxy(entityManager);
}

}

If you use Hibernate Search, have a look at the superclass implementation - you might want to instantiate a FullTextEntityManager directly (as you have it in your classpath - but note that this has not been tested here).

Both implementations are on our Google Code repository, and you can integrate them directly over the following Maven dependency:

<dependency>
<groupId>com.ctp.seam</groupId>
<artifactId>seam-jpa2</artifactId>
<version>1.0.0</version>
</dependency>


You’re now ready to code JPA 2 queries! We’ve already included the meta model generator utility, so let’s activate it for the build:

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>validate</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src/main/hot</source>
<source>${basedir}/target/metamodel</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<outputDirectory>${basedir}/target/metamodel</outputDirectory>
<processors>
<processor>
org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor
</processor>
</processors>
</configuration>
</execution>
</executions>
</plugin>


In order to use the processor plugin, you also need the following Maven repositories in your POM:

<pluginRepository>
<id>annotation-processing-repository</id>
<name>Annotation Processing Repository</name>
<url>http://maven-annotation-plugin.googlecode.com/svn/trunk/mavenrepo</url>
</pluginRepository>
<pluginRepository>
<id>jfrog-repository</id>
<name>JFrog Releases Repository</name>
<url>http://repo.jfrog.org/artifactory/plugins-releases</url>
</pluginRepository>

Run the build, update the project config to include the new source folder - and finally we’re ready for some sample code:

@Name("userDao")
@AutoCreate
public class UserDao {

@In
private EntityManager entityManager;

private ParameterExpression<String> param;
private CriteriaQuery<User> query;

@Create
public void init() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
query = cb.createQuery(User.class);
param = cb.parameter(String.class);
Root<User> user = query.from(User.class);
query.select(user)
.where(cb.equal(user.get(User_.username), param));
}

public User lookupUser(String username) {
return entityManager.createQuery(query)
.setParameter(param, username)
.getSingleResult();
}
}

This is now quite close to Java EE 6 code - all we will have to do is exchange some annotations:

@Stateful
public class UserDao {

@PersistenceContext
private EntityManager entityManager; ...

@Inject
public void init() { ...
}

Enjoy!

Wednesday, April 28, 2010

JBoss Seam Maven Archetype Video Tutorial

While the release of Seam 3 is getting closer, you might still want to do quick prototyping based on a Seam 2 project. Of course this is easiest done with either seam-gen, or if you're like me a Maven user, with an Archetype.

Chris has prepared a video tutorial on how to get started with our Maven WAR archetype (featuring hot deploy, TestNG with embedded JBoss and either RichFaces or ICEFaces inclusion) based on one of my previous articles. Check it out here (best seen in HD [UPDATE: sorry, missed the HD embed]):



Enjoy!

Wednesday, July 1, 2009

JBoss Seam Archetype - now with ICEfaces

Last week I visited Jazoon - and had a great opportunity to see several impressive demos of ICEfaces! Of course I had to immediately get my hands on it, and started to integrate ICEfaces in the JBoss Seam Maven archetype (as described in my previous post).

You can give it a spin by starting a shell and running Maven with the Archetype plugin:

>mvn archetype:generate -DarchetypeCatalog=http://tinyurl.com/jbsarch -DajaxLibrary=icefaces
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
...
Choose archetype:
1: http://tinyurl.com/jbsarch -> jboss-seam-archetype (Archetype for JBoss Seam Projects)
Choose a number: (1): 1
Define value for serverDir: : [your JBoss 5 server location]
Define value for groupId: : [your groupId]
Define value for artifactId: : [your artifactId]
Define value for version: 1.0-SNAPSHOT: : [your version]
Define value for package: : [your package]
Confirm properties configuration:
serverType: jboss5
ajaxLibrary: icefaces
...
Y: : y
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
...


Make sure you don't misspell "icefaces", this will otherwise screw up the application. No input validation in Maven Archetype yet, but I started looking into it ;-)

Again, change to the project directory and build the project:

>mvn package

Now this also executes a sample unit test (fingers crossed it works this time ;-) - thanks to Oscar for the feedback!

Have fun with it! Anybody mind contributing a decent layout template?

Friday, June 19, 2009

Creating a JBoss Seam Maven Archetype

In case you're a regular reader of this blog, I guess you're aware that I'm a frequent user of both Maven and JBoss Seam - and that I'm regularly trying to combine working with both! My usual approach for setting up a new project was either to start with an empty Maven web application project and copying the missing files over, or to start with a seam-gen project and move it into a Maven structure. Both less than ideal...

Maven provides so called archetypes for a quick project setup. As there is not (yet?) an official archetype for Seam projects, I've been working on my own - and here's how you can use it as well!

All you need is
  • a recent version of Maven downloaded (I used 2.0.10)
  • and the Maven executable referenced in your path so you can use it on the console.
Open up a console, cd to your projects directory and type:

mvn archetype:generate -DarchetypeCatalog=http://tinyurl.com/jbsarch

This will start Maven and show the following command line output:

[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO] task-segment: [archetype:generate] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] Preparing archetype:generate
[INFO] No goals needed for project - skipping
[INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.
[INFO] Setting property: velocimacro.messages.on => 'false'.
[INFO] Setting property: resource.loader => 'classpath'.
[INFO] Setting property: resource.manager.logwhenfound => 'false'.
[INFO] [archetype:generate]
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: http://tinyurl.com/jbsarch -> jboss-seam-archetype (Archetype for JBoss Seam Projects)
Choose a number: (1):


The remote archetype catalog contains so far only one archetype (BTW: the jbsarch in tinyurl.com/jbsarch stands for JBoss Seam ARCHetype - hope you can remember this better than the full URL :-) Select the archetype by typing 1 and enter your Maven project properties as well as your JBoss Server directory:

[INFO] snapshot com.ctp.archetype:jboss-seam-archetype:1.0.0-SNAPSHOT: checking for updates from jboss-seam-archetype-repo
Define value for serverDir: : /Developer/Servers/JBoss/jboss-5.1.0.GA
Define value for groupId: : com.ctp
Define value for artifactId: : fluxcapacitor
Define value for version: 1.0-SNAPSHOT: :
Define value for package: com.ctp: : com.ctp.fluxcapacitor
Confirm properties configuration:
serverType: jboss5
serverDir: /Developer/Servers/JBoss/jboss-5.1.0.GA
groupId: com.ctp
artifactId: fluxcapacitor
version: 1.0-SNAPSHOT
package: com.ctp.fluxcapacitor
Y: : y
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 minutes 57 seconds
[INFO] Finished at: Fri Jun 19 19:12:19 CEST 2009
[INFO] Final Memory: 12M/79M
[INFO] ------------------------------------------------------------------------


Note that the serverType property defaults to jboss5. If you have a JBoss 4.2.x installation, quit with n and retype everything (hmm...) and use jboss4 instead.

In case anything fails here, make sure your archetype plugin is at least version 2.0-alpha-4 (I had to delete the local repo info file in the local repository once). Now with your project created, lets build and deploy it!

Aragorn:sandbox thug$ cd fluxcapacitor/
Aragorn:fluxcapacitor thug$ mvn package
[INFO] Scanning for projects...
[INFO] Reactor build order:
[INFO] [fluxcapacitor]
[INFO] [fluxcapacitor :: JBoss Configuration]
[INFO] [fluxcapacitor :: Web Application]


The project currently features two modules: One with the actual web application, and the other one containing JBoss Server configuration files. With the basic archetype you only get a datasource there, but you can also use it to change server ports, define security realms, queues etc.

Environment specific properties are referenced in filter property files. You can find the development filter file in ${artifactId}/environment/filters/${artifactId}-development.properties. The filter file selection happens in the parent POM. It defines a development profile which sets the environment property. Setting this property to test will look for a ${artifactId}/environment/filters/${artifactId}-test.properties filter file.

Note that the parent POM contains your JBoss installation directory hard coded. You might want to factor this out into a developer specific profile when working in a team. Fire up your JBoss instance. Everything should start up well (fingers crossed...) and you can point your browser to the base URL of your application.


Done! Import the project in your favorite IDE and start prototyping!

Unfortunately this is still far away of what seam-gen provides up to now. Maven archetypes have improved over time but are currently still not as flexible to be useful in complex setups:
  • Entering project properties is not even close to be user friendly. At least a description, input validation (enumerations) and reasonable handling of default values might help.
  • Conditional installation of files. If you know how this works - let me know :-) This would be required to distinguish between WAR and EAR projects, and for targeting different applications servers (yeah, like GlassFish!).
In case I find some time to contribute something here, I'll report back. If you have feedback / contributions to the archetype, this is as usual very welcome (the code is available at our Google Code repository)!

Friday, May 29, 2009

JBoss Seam Hot Deploy with Maven - EAR Projects

As requested on some comments on my previous posts (initial post and update), the Seam Hotdeploy Maven plugin now works also with EAR projects.

Some more details on how to configure your project can be found in our Google Code Wiki. The plugin now defines a new packaging type for Seam WAR modules - the only possibility to override the war:war goal in the package phase.

If you're interested in how to override the default lifecycle Maven goal, you can check out the plugin source - something I haven't been able to google easily. Or, maybe wait for a dedicated blog post - stay tuned!

Something you should definitely checkout with the Hot Deploy plugin is the Maven CLI plugin. See the great post from Dan here, or the integration in the sample POM.

As usual, feedback and contributions are very welcome!

Thursday, April 16, 2009

JBoss Seam on Google App Engine - First Steps

[UPDATE] The latest Morjarra release 1.2_13 as well as the latest Appengine SDK 1.2.2 seem to fix a couple of problems described below. My task backlog is actually growing :-) but I hope I find some time to look again how things work with these new versions.

I spent the last three weeks in a repetition course of the Swiss army - far away from any Java code. Naturally my fingers started to itch while reading the announcement of Google that their App Engine now supports Java! So I grabbed my MacBook to enjoy the sun, Java coding and Eastern holidays.

As I usually write web applications with JBoss Seam, I decided to give the framework a try in the Google cloud - preparing for a bumpy road as Seam founds on standards which are mostly listed as either not or not known to be working. The following article describes the (bad) tweaks I had to do to get something basic running - I guess if you start doing serious development, you might hit more walls.

First, install the Eclipse Plugin for App Engine and create a new project. I'll base my descriptions on this initial setup. Switch on sessions in appengine-web.xml:
<sessions-enabled>true</sessions-enabled>

Setting up JSF 1.2

Download the latest Mojarra 1.2 release [1.2_12] and put it in the WEB-INF/lib directory. You can configure the Faces servlet in web.xml as usual
<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>*.seam</url-pattern>
</servlet-mapping>

Starting Jetty with only this will fail due to some incompatibilities of Jetty and Mojarra. As with Seam we will need JBoss EL anyway, we can configure Mojarra to use the JBoss EL ExpressionFactory. Add the JBoss EL JAR to WEB-INF/lib and the following XML to your web.xml:
<context-param>
<param-name>com.sun.faces.expressionFactory</param-name>
<param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
</context-param>

Now it's already patch time. Jetty in the Google environment seems to have a bug in its Servlet API implementation, missing the ServletContext.getContextPath() method (new in version 2.5). Also, Mojarra tries to be clever about initialization work and uses Threads in the ConfigManager - the App Engine SecurityManager will let the whole thing blow up. Something similar happens in the JBoss ReferenceCache class. All patched classes can be found here. Drop the Java code in your source folder, Jettys classloader will pick it up.

This will at least make the whole thing start up. I also added facelets (JAR file, view handler in faces-config.xml and view suffix in web.xml).

Unfortunately, using RichFaces components is an absolute no go on App Engine. RichFaces is full of references to AWT or JAI classes, which Google blocks completely. If anybody wants to try ICEFaces - good luck!

Adding Seam

Now it's time to add all the Seam stuff. Quite a couple of JARs to put into the application. I basically used Seam 2.1.1.GA and libraries from JBoss 4.2.3.GA. The complete list of what should go into WEB-INF/lib is shown in the screenshot below.



JTA is not even on the list of APIs Google gives a recommendation, so let's fall back on Java SE behavior. Configure components.xml with:
<transaction:entity-transaction entity-manager="#{entityManager}"/>


Although we cannot use Hibernate as persistence provider, Seam has a couple of dependencies to it (e.g. when using Hibernate Validators). As soon as we have it in the classpath, Seam activates its HibernatePersistenceProvider component. This will behave pretty bad, and for simplicity we just override this component:
@Name("org.jboss.seam.persistence.persistenceProvider")
@Scope(ScopeType.STATELESS)
@BypassInterceptors
@Install(precedence = Install.APPLICATION,
classDependencies={"org.hibernate.Session", "javax.persistence.EntityManager"})
public class OverrideHibernatePersistenceProvider extends PersistenceProvider {
}

Now also add a persistence provider as described in the Google docs and disable DataNucleus checking for multiple PersistenceContextFactory instantiations with this property in appengine-web.xml.
<property name="appengine.orm.disable.duplicate.emf.exception" value="true"/>


As before, also Seam needs a couple of patches. Main reasons for those are references to javax.naming.NamingException (which is not white listed, credits to Toby for the hint) and session/conversation components not implementing Serializable correctly. The last point is probably something not hitting Seam or your application the last time.

Identity

As a next step I tried to add some users to the application. Seam's Identity module builds around the javax.security.auth.Subject and Principal classes. Even though those classes are white listed, the SecurityManager blocks any attempt to add a Principal to a Subject. Well, how useful is that... As a quick fallback I integrated the Google Accounts API:
@Name("org.jboss.seam.security.identity")
@Scope(SESSION)
@Install(precedence = Install.APPLICATION)
@BypassInterceptors
@Startup
public class AppEngineIdentity extends Identity {

private static final long serialVersionUID = -9111123179634646677L;

public static final String ROLE_USER = "user";
public static final String ROLE_ADMIN = "admin";

private transient UserService userService;

@Create
@Override
public void create() {
userService = UserServiceFactory.getUserService();
}

@Override
public boolean isLoggedIn() {
return getUserService().isUserLoggedIn();
}

@Override
public Principal getPrincipal() {
if (isLoggedIn())
return new SimplePrincipal(getUserService().getCurrentUser().getNickname());
return null;
}

@Override
public void checkRole(String role) {
if (!isLoggedIn())
throw new NotLoggedInException();
if ((ROLE_ADMIN.equals(role) && !getUserService().isUserAdmin()) || !ROLE_USER.equals(role))
throw new AuthorizationException(String.format(
"Authorization check failed for role [%s]", role));
}

@Override
public boolean hasRole(String role) {
if (!isLoggedIn())
return false;
return ((ROLE_ADMIN.equals(role) && getUserService().isUserAdmin()) || ROLE_USER.equals(role));
}

@Override
public String getUsername() {
if (isLoggedIn())
return getUserService().getCurrentUser().getNickname();
return null;
}

public String createLoginURL(String destination) {
return getUserService().createLoginURL(destination);
}

public String createLogoutURL(String destination) {
return getUserService().createLogoutURL(destination);
}

public User getUser() {
if (isLoggedIn())
return getUserService().getCurrentUser();
return null;
}

private UserService getUserService() {
if (userService == null)
userService = UserServiceFactory.getUserService();
return userService;
}

}

Both create... methods can be used in the UI for generating login/logout URLs. Destination defines the URL the user gets redirected after successful login/logout. Also make sure that the identity configuration is removed from components.xml

Wrap up

Running this setup should give you a base for a very simple Seam app like in the screenshot below.



This first step has not been doing any persistence work, and my first tries with DataNucleus were not as straight forward as I had expected from a JPA implementation. Hope Google will catch up here with something more mature. Also, even the simple setup required a couple of nasty tweaks on the frameworks. Another big hurdle here are the runtime differences from production to local environment. For some serious work on App Engine, it's so far more recommendable to look into GWT.

Anyway, if you found this useful - looking forward to hear from your next steps in the cloud.

Friday, March 20, 2009

JBoss Seam Hot Deploy with Maven - Update

I just published an updated version of the Seam Hotdeploy Maven Plugin (see my previous post here). For a detailed description on how to use it, have a look at the Google Code Wiki. I also updated the sample Maven POM featuring JBoss Seam 2.1.1.GA.

The new version should be easier to configure and play nicer with e.g. the Eclipse Maven plugin (I'm not using NetBeans/IntelliJ too often, but I hope this works also fine there). The new Eclipse builder configuration looks like shown in the image below:



Note: The Eclipse Maven plugin using its embedded Maven had somehow problems with the extended lifecycle of the Hotdeploy Plugin. Using an external Maven installation (2.0.10) felt not only faster but was also not having problems with it.

Friday, January 30, 2009

Portal Update January 2009

After the snowy december we are back with new posts this year and we are looking forward to an interesting year! Java EE 6 is the late christmas present for many of us Java aficionados and it is going to be released probably around JavaOne in June...

In the portal corner, the following was important this month:
  • SUN: no updates

  • Liferay:
    - One UI to rule them all: Check out this introduction of a new management console in Liferay Portal. This portal product is definitely going to be more and more interesting among the free portal products!

  • Oracle:
    - Oracle Fusion Middleware Radio has released some talks about portal product roadmaps. No much news if you followed all our portal updates so far ;-). But if you are interested in to listen to them, here they are:
    Oracle WebLogic Portal Roadmap (formerly BEA WebLogic Portal)
    Oracle WebCenter Interaction Roadmap (formerly BEA AquaLogic User Interaction)
    Oracle Portal
    Roadmap

  • JBoss:
    - Release of JBoss Portal 2.7.1 which is a maintenance release for the 2.7 series that support JSR-286 portlets. The detailed release notes can be found here.
    - The JBoss Portlet Bridge has reached Beta 6 and it integrates Seam and RichFaces. Documentation on this Beta release can be found here.
    - Next-Gen Identity API for JBoss Portal is in public review. Check their documentation here.

  • eXo:
    - eXo Portal 2.5.1, a maintenance release, is now available and has finalized its support of Right-To-Left languages like arabic. See video here.
    - eXo Portal and Spring Security: This is a very nice documentation released in January about how to integrate Spring Security 2.5.x into eXo Portal.

  • Other portal and Java related quick news:

    Jan-26: JSR-299: WebBeans is dead. Long live Java Contexts and Dependency Injection!

    Jan-23: Java EE 6 has gone Public Review!
    - WebBeans (Gavin King, JSR 299, @TA )
    - Bean Validation (Emmanuel Bernard, JSR 303, @TA )
    - JSF 2.0 (Ed Burns & Roger Kitain, JSR 314, @TA )
    - Servlet 3.0 (Rajiv Mordani, JSR 315, @TA )
    - JPA 2.0 (Linda DeMichiel, JSR 317, @TA )
    - EJB 3.1 (Ken Saks, JSR 318 @TA )
    - JCA 1.6 (Binod PG & Sivakumar Thyagarajan, JSR 322, @TA )

    Jan-18: Performance Analysis: VisualVM 1.1 has been released!
    Dec-16: JBoss Application Server 5.0 has gone GA. So JBoss has finally joined the list of "Java EE 5 Compatible Implementations".

  • NetBeans:
    - Portal Pack 3.0 Beta is now available
  • IBM: no portal news
  • Eclipse: no portal news
  • IntelliJIDEA: no portal news

Saturday, December 6, 2008

Using JBoss Seam Hot Deployment with a Maven Build

[UPDATE: The new plugin version is documented here]

As you might have read in previous posts, one of our favorite web application stacks consists of JBoss Seam. While combining this with our favorite tooling suite consisting of Eclipse and Maven, we had to realize that Seam has a much longer tradition with Ant, and Maven support is only slowly moving in. Today I want to share some ideas on how to tackle the combination issues.


While JBoss Tools provides a nice code hot deployment feature when working with Seam on JBoss, things can get a little hairy when using a Maven build with the project. Seam uses a special classloader and relies on monitoring the WEB-INF/dev folder for changed classes - where Maven is completely unaware of this and uses the standard WEB-INF/classes folder to deploy all classes in the project. More than once I've seen the situation where JBoss Tools and Maven got in each others way, ending with JBoss rejecting to restart the application due to "duplicate components". The reason was that Eclipse hot deployed classes to WEB-INF/dev where Seam expects them, but Maven being completely unaware of it packaging all classes in the common WEB-INF/classes folder.

In order to avoid trouble and also to allow using hot deployment from a command line build, I experimented with several approaches to achieve hot deployment with Maven. The main challenges here are:
  • Distinguishing compiler output: The Maven compiler plugin by default compiles everything in the same output folder. We need to be able to distinguish between hot and regular deployable code.
  • Publish updated resources to JBoss, including domain model classes, seam components as well as XHTML pages.
  • Make sure Seam unit tests still compile.
The solution I came up with was to write a new Maven plugin which extends the Maven compiler plugin with a new hotdeploy:compile goal and the following functionalities (source code as well as a simple Maven 2 repository is available in our Google Code account):
  • Hook into the compile phase and compile the source code to a dedicated directory. The source code location is configurable as well as the output directory.
  • Remove outdated hot deployable class files from the JBoss deployment. This is necessary for the Seam classloader to pick up the changes, only updating the file is not enough.
The plugin configuration is straight forward as it's based on the regular compiler plugin and is shown in the XML snippet below. Note that it is recommended to create a profile for this build setup as both your continuous build as well as your operations team might not care too much about using hot deployment.
<plugin>
<groupId>com.ctp.seam.maven</groupId>
<artifactId>maven-hotdeploy-plugin</artifactId>
<version>0.1.0</version>
<configuration>
<source>${java.source.version}</source>
<target>${java.source.version}</target>
<hotdeployOutputDirectory>${directory.hotdeployables}</hotdeployOutputDirectory>
<includes>
<include>**/session/**</include>
</includes>
<deployDirectory>
${directory.deploy.jboss}/${build.finalName}.${project.packaging}
</deployDirectory>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>

The main additions to a regular compiler plugin setup are:
  • This variant uses the common /src/main/java source folder and uses in- and excludes to distinguish the hot deployable sources. Alternatively you can set the sourceDirectory configuration value pointing to a dedicated source folder (similar as JBoss Tools does the setup for you). Don't forget that this requires then the Build Helper Maven plugin to add source folders to your regular build.
  • The deployDirectory is a reference to your exploded application directory inside the JBoss deploy folder. This is needed in order to remove outdated hot deployable classes.
  • You can specify the hotdeployOutputDirectory directory where the code should be compiled to. Note that this defaults to the value shown in the example - feel free to skip this part. The plugin will compile into this folder after appending a WEB-INF/dev to the path.
The rest of the setup we can do by simply reconfiguring existing plugins. Note the Google Code Wiki contains a complete sample POM ready for you to start your own Seam webapp. The details on the configuration are:
  • Reconfiguring the WAR plugin so the webappDirectory points inside our JBoss deploy folder. Maven uses this folder to assemble the WAR file, which perfectly corresponds to an exploded deployment. Additionally we define our hot deployment compilation folder as a webResource, which will copy our compilation exactly to the WEB-INF/dev folder in the exploded deployment.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>${directory.hotdeployables}</directory>
<filtering>false</filtering>
</resource>
</webResources>
<webappDirectory>
${directory.deploy.jboss}/${build.finalName}.${project.packaging}
</webappDirectory>
</configuration>
</plugin>

  • Similarly, we have to add our output folder to the test classpath. This is simplest done by adding it as a resource folder, which will copy the classes to the target/test-classes directory before the test classes are compiled:
<testResources>
<testResource>
<directory>${directory.hotdeployables}/WEB-INF/dev</directory>
<filtering>false</filtering>
</testResource>
...
</testResources>

If you want to run a similar luxury setup as you get from JBoss Tools, you can add a Maven builder to your project. Simply go to the Eclipse Project properties and click "New" on the "Builders" properties. The new Maven build should look like the following:



Make sure that the following conditions apply to your Eclipse setup:
  • The JRE you have configured is actually a JDK - otherwise the builder will fail. Once you see that the builder does not fail, you can also switch off the console allocation in the "Common" tab.

  • Also take care that you always build with your specific hot deploy profile - otherwise make sure to clean before using the build again.

  • Assert that Eclipse uses a dedicated output folder and not the Maven target/classes.

Unfortunately when activating the builder Eclipse might prompt you when editing files and (depending on the speed of your machine) hang for a second or two after saving a file. Still, this is much better than waiting a minute for your JBoss server to restart.

Happy coding!

Tuesday, June 10, 2008

Targeting JBoss Seam for Multiple Application Servers

Not all application servers are equal - something you realize quickly in a consultant's life working in different customer environments. The "developer friendliness" varies broadly here - I guess waiting for the container to deploy, go down and up again is a phenomenon many of the web developers out there know too well...

A stack that works for me very efficient is JBoss Seam on JBoss application server. Since its integration with JBoss Tools, the code-deploy cycle goes extremely smooth. But how can we leverage this while targeting other application servers?

After some experiments, I came up with a solution based on Maven. In general, the deployment of Seam distinguishes in library dependencies and some config files. For example is Hibernate the default persistence provider on JBoss, but not on WebLogic - but using Hibernate has the advantage of using Hibernate Validators, which integrates in Seam very comfortable with JSF validation. Maven allows to create different profiles, which contain different dependency sets tailored for another application server. The differences for running Seam in different containers are well described in the Seam documentation.

pom.xml:
<profiles>
<profile>
<id>weblogic10</id>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>${hibernate.version}</version>
<exclusions> ... </exclusions>

...

</profile>
</profiles>
Integrating this in JBoss Tools turnes out to be a little trickier. The best approach seemed to be to start with a Seam project wizard in the Maven source structure:
  • Java Source Directory: src/main/java
  • Content Directory: src/main/webapp
The project needs then to be 'mavenized' by adding the Eclipse Maven 2 plugin if not already installed and a POM to the project. Getting the dependencies correct can be a little tedious with all the exclusions - best is to add just one by one and compare with the Web App Libraries that come out of the wizard. Once this is done, the libraries in WEB-INF/lib can be deleted and the Maven dependencies added in the Project J2EE Dependencies dialog. As a nice side-effect, this Maven setup makes it extremely easy to upgrade your libraries. For example, upgrading RichFaces from 3.2.0 to 3.2.1 was just a 1-character change in the POM!

We have now a clean separation between dependencies, but still the config files need different treatments. I went for a solution on adding three subfolders to src/main/resources:
  • common for, well, common resource files like message bundles and Drools rule files
  • jboss and e.g. weblogic for the files requiring different versions for applications servers, namely the components.xml and persistence.xml files (using Hibernate on WebLogic 10 requires a different configuration - the transaction manager lookup and the query factory class need to be changed).
This works now fine in JBoss Tools and you can start coding. Still, to run the Maven build we need some additional fixes as we changed the Maven default structure on the source files (JBoss Tools uses src/main/java/action and src/main/java/model) and on the resources:

pom.xml:
<build>
<sourceDirectory>
${basedir}/src/main/java/model
</sourceDirectory>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src/main/java/action</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
This configures the two source folders in JBoss Tools - a little verbose, but Maven usually knows only one source folder. For the resource folders, we need to configure the build settings in the distinct Maven profiles:

pom.xml:
<build>
<resources>
<resource>
<directory>src/main/resources/common</directory>
</resource>
<resource>
<directory>src/main/resources/weblogic</directory>
</resource>
</resources>
</build>
We can now test for example the WebLogic profile in a continuous build, deploying the WAR file on an integration server.

The resulting project structure in Eclipse is shown in the screenshot below: