Thursday, December 27, 2007

Kooking Seam-gen with Glassfish

Seam is an application framework for Enterprise Java. It simplifies enterprise application development. However configuring seam to work with appservers other that Jboss is a complex process. Following are the summary of issues I faced when working with the code generated by Seam-Gen
  1. Seam-gen ejb version does not really generate EJB code
  2. JNDI naming conventions for GLASSFISH does not follow that of Jboss. Each ejb has to be mentioned at-lease twice in the web.xml
  3. Seam proposes that the injection of Entitymanger should be left to Seam. One of the argument is that seam has better transaction management which is compatible with Seam conversations.
    • This requires that we enable Seam Managed Transactions
    • Consequence is EjbSynchronizations provided by Seam has to be deployed
  4. Seam managed transactions require Manual Flush mode to be enabled. Incidentally Hibernate is the only JPA provider which supports manual flush
Conclusion :
Seam provides power full features for Enterprise development. There are few startup issues while working in non-jboss servers. Give the benefits of seam the limitations are negligible.
Note : For detailed discussion on how to address the above issues refer to my previous blog Kooking Seam with Glassfish

Kooking Seam with Glassfish

Seam is an application framework for Enterprise Java. The documentation claims that Seam works in any Java EE application server. Lets us investigate how easy /tough it is to kook an enterprise application on Glassfish .


The recipe :

Presentation Layer : JSF, Faceletes,Jboss EL,Xhtml

Business Layer : StateFull session beans

Persistence layer : JPA (Toplink,Hibernate)

Frame-Work :Jboss-Seam

Serving ON : Glassfish (V2)


Ingredients :

Seam FrameWork : jboss-seam-2.0.0.GA( Includes- Seam,Seam Gen,Rich Faces,Hibernate,Facelets)

Netbeans IDE: netbeans-6.0-linux(Includes- GlassFish V2,Toplink,Sun Jdk)


The primary objective is to write a quick solution which can perform basic CRUD operations. It is assumed that you have the database tables already created. Shortest path to get there is use Seam-Gen. Seam-Gen can generate code for web-application as well as enterprise application. The generated code works out-of the box provided you use Jboss Application server. We will have to add few spices and salt to make it tasty on GlassFish. We will walkthrough all the tricks needed to make it work with GlassFish.

Secondary objective is to be able to work in my own kitchen. In my kitchen the stove is made of Netbeans.



  1. Setup IDE -- Prepare you IDE

  2. Entities Code Generation -- There are two code generators available

  3. Jsf Code Generation – Views

  4. Setup your project -- Netbeans in action

  5. JNDI naming for EJB -- Glassfish Issue

  6. EjbSynchronizations -- GlassFish issue - JPA issue


Setup IDE

  • Setup a new database connection to Mysql. (Tip Use Services window)

  • Copy Mysql JDBC drive to

  • Go to Tools|Libraries create a new library seam. Add following jars

  • boss-seam-2.0.0.GA/lib/activation.jar
    jboss-seam-2.0.0.GA/lib/ant-antlr.jar
    jboss-seam-2.0.0.GA/lib/antlr-runtime.jar
    jboss-seam-2.0.0.GA/lib/antlr.jar
    jboss-seam-2.0.0.GA/lib/asm-attrs.jar
    jboss-seam-2.0.0.GA/lib/asm.jar
    jboss-seam-2.0.0.GA/lib/cglib-nodep.jar
    jboss-seam-2.0.0.GA/lib/cglib.jar
    jboss-seam-2.0.0.GA/lib/commons-beanutils.jar
    jboss-seam-2.0.0.GA/lib/commons-collections.jar
    jboss-seam-2.0.0.GA/lib/commons-digester.jar
    jboss-seam-2.0.0.GA/lib/commons-lang.jar
    jboss-seam-2.0.0.GA/lib/commons-logging.jar
    jboss-seam-2.0.0.GA/lib/core.jar
    jboss-seam-2.0.0.GA/lib/dbunit.jar
    jboss-seam-2.0.0.GA/lib/dom4j.jar
    jboss-seam-2.0.0.GA/lib/drools-compiler.jar
    jboss-seam-2.0.0.GA/lib/drools-core.jar
    jboss-seam-2.0.0.GA/lib/groovy-all.jar
    jboss-seam-2.0.0.GA/lib/gwt-servlet.jar
    jboss-seam-2.0.0.GA/lib/hibernate-annotations.jar
    jboss-seam-2.0.0.GA/lib/hibernate-commons-annotations.jar
    jboss-seam-2.0.0.GA/lib/hibernate-search.jar
    jboss-seam-2.0.0.GA/lib/hibernate-validator.jar
    jboss-seam-2.0.0.GA/lib/itext.jar
    jboss-seam-2.0.0.GA/lib/janino.jar
    jboss-seam-2.0.0.GA/lib/javassist.jar
    jboss-seam-2.0.0.GA/lib/jboss-el.jar
    jboss-seam-2.0.0.GA/lib/jboss-seam-debug.jar
    jboss-seam-2.0.0.GA/lib/jboss-seam-gen.jar
    jboss-seam-2.0.0.GA/lib/jboss-seam-ioc.jar
    jboss-seam-2.0.0.GA/lib/jboss-seam-mail.jar
    jboss-seam-2.0.0.GA/lib/jboss-seam-pdf.jar
    jboss-seam-2.0.0.GA/lib/jboss-seam-remoting.jar
    jboss-seam-2.0.0.GA/lib/jboss-seam-ui.jar
    jboss-seam-2.0.0.GA/lib/jboss-seam.jar
    jboss-seam-2.0.0.GA/lib/jbpm-jpdl.jar
    jboss-seam-2.0.0.GA/lib/jcaptcha-all.jar
    jboss-seam-2.0.0.GA/lib/jcommon.jar
    jboss-seam-2.0.0.GA/lib/jfreechart.jar
    jboss-seam-2.0.0.GA/lib/jgroups.jar
    jboss-seam-2.0.0.GA/lib/lucene-core.jar
    jboss-seam-2.0.0.GA/lib/mvel14.jar
    jboss-seam-2.0.0.GA/lib/quartz.jar
    jboss-seam-2.0.0.GA/lib/richfaces-api.jar
    jboss-seam-2.0.0.GA/lib/richfaces-impl.jar
    jboss-seam-2.0.0.GA/lib/richfaces-ui.jar
    jboss-seam-2.0.0.GA/lib/jsf-facelets.jar
    jboss-seam-2.0.0.GA/lib/hibernate-entitymanager.jar
    jboss-seam-2.0.0.GA/lib/hibernate.jar
    jboss-seam-2.0.0.GA/lib/jboss-common-core.jar
    jboss-seam-2.0.0.GA/examples/jpa/lib/mc/concurrent.jar

Entities Code Generation
There are two ways to generate entity classes. a) Netbeans b) Seam-Gen. Both of them have limitations. What it means is you will end up making few changes in the generated code any way you choose.
Netbeans generator:
Bonus : equals(Object obj),toString(), hashCode(), @NamedQueries
Limitations : Hibernate Validations, ID generation strategy
Seam-gen :
Bonus: Annotations for Hibernate Validations,ID Generating Strategy
Limitations : Faulty @Temporal specially for TIMESTAMP, NamedQueries, equals(Object obj),toString(), hashCode()

I choose Seam-gen generated code for entities
MYSQL BUG : When you are using decimal data type in mysql hibernate refuses to start. To fix this problem follow this link


Jsf Code Generation -- Views:

Seam-gen can generate jsf views for all the tables in the database. There is a nice seam plugin for Netbeans. I don't recommend to use it. It does not work with seam2.0. I have generated views and entities using the ant script provided in the seam package.



It generates most of the code needed for basic CRUD operations. Following artifacts are generated
1) Entity beans
2) Action Classes (Extends helper classes form seam more on this below)
3) Xhtml pages - Each entity has

Seam-gen can generate code with ejb option and with-out ejb options. If you choose to build a web applications select non-ejb options. You can open the generated project in Netbeans and run it as is.( TIP: In Netbeans use new web project from existing source)
The issues are with ejb version. One would expect the generated action classes are EJBS. BUT they are classes still live in web-application. So we have to migrate these actions piece by piece to state-full session beans.

5) Setup your project

  • Create a web application {generated} : This is a reference project. Use the project generated by seam-gen

  • Create an enterprise application{seamcrud}. Netbeans creates two subprojects {seamcrud}-ejb and {seamcrud}-war

    • Add seam library to {SeamCrud-ejb} project.

    • Create entities from database. This helps you setup the persistence related artifacts. (TIP : Netbeans verifies that you have chooses right app server and database etc..)

  • Remove all the entities from {SeamCrud-ejb} Copy entities from {generated} (TIP : Netbeans has lovely refactoring features)

  • Copy all configuration files and xhtml , xml file from

  • Deploy the {SeamCrud} application and ensure that you are able to execute the default index.jsp.

FUN STARTS



The {seamcrud} application contains entities and views. But it doesn't yet have seam-actions. Seam-gen generates action which are good enough for a web application. Here we shall write actions using state-full session beans.

Options One : Create new set of SFSB actions . You will have to write new views

Option Two : Modify generated actions. You will have to extend few of the seam classes. (TIP: Purists may want to avoid this. Remember that our prime objective is the manage CRUD operations. I feel the if CRUD can be auto-generated we can live with few limitations. and focus on business logic ,work flow and rules for the application. Using the above approach one can develop CRUD application in no time)



We have to write Four new artifacts for each entity.

1) {entity}List extends org.jboss.seam.framework.EntityQuery (TIP: Copy this class from {generated} )

2) {entity}Home extends org.jboss.seam.framework.EntityHome (TIP: Copy this class from {generated} )

3) {entity}ListLocal - Local interface for SFSB(TIP: There is no point in using remote interfaces. Seam is not yet certified to handle remote ejbs)

4) {entity}HomeLocal - Local interface for SFSB

5) Components.xml (2) one for SeamCrud-ejb and one for SeamCrud-war



Sample {entity}List class

1) @Name("currencyList")
2) @PersistenceUnit(name="JPayroll-ejbPU", unitName="JPayroll-ejbPU")
3) @Stateful
public class CurrencyList extends EntityQuery implements CurrencyListLocal{

private static final String[] RESTRICTIONS = {
"lower(currency.name) like concat(lower(#{currencyList.currency.name}),'%')",
"lower(currency.isoCode) like concat(lower(#{currencyList.currency.isoCode}),'%')",};

private Currency currency = new Currency();
4) @In EntityManager em;
@Override
public String getEjbql() {
return "select currency from Currency currency";
}

@Override
public Integer getMaxResults() {
return 25;
}
public Currency getCurrency() {
return currency;
}
@Override

public List

return Arrays.asList(RESTRICTIONS);

}

5) @Remove @Destroy

public void destroy(){}

public EntityManager getEntityManager() {

return em;

}



1) Every seam components should have a name. Here it is 'currencyList'. In web application seam registers this component automatically. Its tricky in ejb. EJBs are looked up via JNDI. So we have to tell seam what will be the jndi url for this SFSB. Add following line in the components.xml


<ejb-local-ref>

<ejb-ref-name>CurrencyList</ejb-ref-name>

<ejb-ref-type>Session</ejb-ref-type>
<local>com.blogspot.jkook.jpayroll.beans.CurrencyListLocal</local>
<ejb-link>JPayroll-ejb.jar#CurrencyList</ejb-link>
</ejb-local-ref>
<ejb-local-ref>
<ejb-ref-name>currencyList</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>

<local>com.blogspot.jkook.jpayroll.beans.CurrencyListLocal</local>
<ejb-link>JPayroll-ejb.jar#CurrencyList</ejb-link>
</ejb-local-ref>


(TIP: If the seam name and ejb name are different you will need two entries in web.xml)

Seam still cannot access your ejbs .. The trick is to add an empty seam.properties file to your ejb.jar. It has to be under “Configuration Files” in the {seamcrud-ejb} project. On server startup seam locates all the seam components and registers their names. However it does not know which jars to look for. Hence it requires seam.properties in the jar file where the seam components exists.



2) As mentioned above Seam expects all ejb resources be available via JNDI lookup. This line publishes the EntitiyManager as a JNDI resource. Seam will use this for managing the transactions. (TIP: Seam managed transactions cut through the seam conversations. Which is not possible with default transactions management provided by the JPA provider)

3) Indicates that this class is StateFull Session Bean

4) @In EntityManager em; We are going to use seam manged persistence and seam managed transactions. Injecting the EntityManager is now handled by Seam. It requires corresponding entry in components.xml in the ejb project.

<persistence:managed-persistence-context
name="em" auto-create="true"
persistence-unit-jndi-name="java:comp/env/JPayroll-ejbPU"/>
<transaction:ejb-transaction/>

The above lines tell seam how to locate its EntityManager.

(TIP: managed-persistence-context name in the xml and the variable name used for EntityManager should be same)

5) @Remove is needed by StateFull bean, @Destroy is needed for managing seam conversations



Sample {entity}Home. (TIP : @ PersistenceUnit is needed for each ejb. I have't yet figured how to make it a global declaration. If you are using Hibernate JPA the following line has to be added to the persistence.xml )

@Name("currencyHome")

@PersistenceUnit(name="JPayroll-ejbPU", unitName="JPayroll-ejbPU")

@Stateful

public class CurrencyHome extends EntityHome

@In EntityManager em;

public void setCurrencyId(Long id) {

setId(id);

}

public Long getCurrencyId() {

return (Long) getId();

}

@Override

protected Currency createInstance() {

Currency currency = new Currency();

return currency;

}

public void wire() {}

public boolean isWired() {return true;}

public Currency getDefinedInstance() {

return isIdDefined() ? getInstance() : null;

}

@Override

public EntityManager getEntityManager() {

return em;

}

@Remove @Destroy

public void destroy(){}

}





Sample {Entity}HomeLocal

public interface CurrencyHomeLocal {

public void setCurrencyId(Long id);

public Long getCurrencyId();

public void wire();

public boolean isWired();

public Currency getDefinedInstance();

public javax.persistence.EntityManager getEntityManager();

public void destroy();

public void create();

public Object getInstance();

public boolean isManaged();

public String update();

}

Sample {Entity}ListLocal

public interface CurrencyListLocal {

public java.lang.String getEjbql();

public java.lang.Integer getMaxResults();

public com.blogspot.jkook.jpayroll.entities.Currency getCurrency();

public void destroy();

public java.util.List

public String getOrder();

public void setOrder(String order);

public Long getResultCount();

public Object getSingleResult();

public boolean isNextExists() ;

public void refresh() ;us

public void first() ;

public Integer getFirstResult() ;

public Long getLastFirstResult() ;

public int getNextFirstResult();

public Integer getPageCount();

public int getPreviousFirstResult();

public void validate();

public List getResultList();

public boolean isPreviousExists();

public javax.persistence.EntityManager getEntityManager();

}

6) EjbSynchronizations : This is needed for implementing call backs on your transactions. Reference to this has to be provided to Seam. Seam.jar has few ejbs. One of them is EjbSynchronizations. Add refernece of this ejb in the web.xml.

<ejb-local-ref>
<ejb-ref-name>EjbSynchronizations</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>org.jboss.seam.transaction.LocalEjbSynchronizations</local>
<ejb-link>EjbSynchronizations</ejb-link>
</ejb-local-ref> Addtionally you have to declare the ejb in ejb-jar,xml

Finally a Secret : I stared the exercise with Toplink JPA provider. Ended using Hibernate JPA provider. Apparently Toplink does not support Manual Flush mode. Seam claims to be able to perform better transaction management especially that for seam conversations. The only JPA provider which gives this flexibility is Hibernate

End Result

I have an enterprise application running on glassfish with following features

  1. Supports Create, Update , Delete, List operations on the {entity}

  2. Provides Paging, Sorting , Search

  3. Uses StateFull session beans

  4. Leverages Seam Conversations

  5. Most of the code is generated

  6. JSF,Facelets,RichFaces(AJAX)



Conclusion

So far experience with seam has been good. With or with out Seam-Gen a developer can built applications quickly. Tools like Netbeans take the whole equation to another level. Following seemingly simple features are still desired from Jboss-SEAM.

  1. Good documentation of how to handle nuances of the popular application servers.

  2. Seam-Gen generate ejb classes and interfaces.