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.
Setup IDE -- Prepare you IDE
Entities Code Generation -- There are two code generators available
Jsf Code Generation – Views
Setup your project -- Netbeans in action
JNDI naming for EJB -- Glassfish 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
Supports Create, Update , Delete, List operations on the {entity}
Provides Paging, Sorting , Search
Uses StateFull session beans
Leverages Seam Conversations
Most of the code is generated
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.
Good documentation of how to handle nuances of the popular application servers.
Seam-Gen generate ejb classes and interfaces.
No comments:
Post a Comment