The aim: Set up a simple Java EE 6 project on GlassFish v3 in no time at all. The project must include: email, JMS, JPA, web and EJB (session bean, message driven bean and a timer bean). It must also include security and transactions.
Sounds like a lot, but thanks to Java Enterprise Edition version 6, setting up a project like this and configuring all the resources in the Application Server are really easy! I chose GlassFish because its open source, has a useful little Admin Console and I’ve never developed with it before.
Before I started I downloaded Java SE 6 (update 20), Mysql Server, the Mysql JDBC Driver and the GlassFish Tools Bundle for Eclipse, which is a WTP Version of Eclipse 3.5.1 with some specific bundles for developing and deploying on GlassFish.
The process I wanted to implement was simple: a user goes to a website, clicks a link to a secure page and logs in, after which a message is persisted to the database and an asynchronous email gets sent. The user is shown a confirmation. In the background theres also a task which reads new messages from the database and updates them so they are not processed a second time.
The design was to use a servlet for calling a stateless session EJB, which persists a message using JPA and sends a JMS message to a message driven bean for asynchronous processing. The MDB sends an email. A timer EJB processes and updates any messages it finds in the database. Additionally I set a requirement to use only the newest technologies in Java EE 6, my servlet and beans were configured using annotations. The following sequence diagram shows the rough design:
The first challenge was to configure my resources in GlassFish. I started the preinstalled server instance which is shipped with this special Eclipse and opened the admin console at localhost (got the port number from the log console).
Without deploying any apps, I configured the following:
1) JDBC Connection Pools, which contain the connection details to the database – one for data, one for a security realm. When creating them, I used default settings apart from:
Pool for Data:
<jdbc-connection-pool datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" res-type="javax.sql.XADataSource" name="testPool" ping="true">
<property name="URL" value="jdbc:mysql://localhost:3306/test" />
<property name="Url" value="jdbc:mysql://localhost:3306/test" />
<property name="User" value="root" />
<property name="Password" value="password" />
.
.
<jdbc-connection-pool>
Pool for Security Realm:
<jdbc-connection-pool driver-classname="com.mysql.jdbc.Driver" res-type="java.sql.Driver" name="realmPool" ping="true">
<property name="URL" value="jdbc:mysql://localhost:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8" />
<property name="password" value="password" />
<property name="user" value="root" />
</jdbc-connection-pool>
2) JDBC Resources, which is where you set the JNDI names for your JDBC Connection Pools.
<jdbc-resource pool-name="testPool" jndi-name="jdbc/test" /> <jdbc-resource pool-name="realmPool" jndi-name="jdbc/realm" />
3) JMS Resource – one connection factory for sending messages, and one destination which is the queue which references a physical destination in the jms service (which gets automatically added further down in the admin console under the JMS Service).
<admin-object-resource res-adapter="jmsra" res-type="javax.jms.Queue" description="a test queue" jndi-name="jms/destinationResource"> <property name="Name" value="test_destination" /> </admin-object-resource> <connector-resource pool-name="jmsConnectionPool" jndi-name="jmsConnectionPool" /> <connector-connection-pool name="jmsConnectionPool" resource-adapter-name="jmsra" is-connection-validation-required="true" connection-definition-name="javax.jms.ConnectionFactory" transaction-support="XATransaction" />
4) A Javamail Session, so that my app could send email over POP3.
<mail-resource host="localhost" store-protocol="POP3" store-protocol-class="com.sun.mail.pop3.POP3Store" jndi-name="mail/Session" from="boss@maxant.co.uk" user="boss"> <property name="mail-password" value="boss" /> </mail-resource>
5) Under Configuration / Security, I set up a JDBC realm which used the realm database connection pool which was configured above.
<auth-realm name="database-realm" classname="com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm"> <property name="jaas-context" value="jdbcRealm" /> <property name="datasource-jndi" value="jdbc/realm" /> <property name="user-table" value="party" /> <property name="user-name-column" value="login" /> <property name="password-column" value="password" /> <property name="group-table" value="role" /> <property name="group-name-column" value="role" /> <property name="db-user" value="root" /> <property name="db-password" value="password" /> <property name="digest-algorithm" value="none" /> </auth-realm>
Here is the domain.xml file (GlassFish’s main configuration file, located under either %glassfishv3_install%/glassfish/domains/domain1/config if you installed the server, or under %GlassFish-Tools-Bundle-For-Eclipse-1.2-Install&/workspace/.metadata/.plugins/com.sun.enterprise.jst.server.sunappsrv92/domain1/config if you installed the GlassFish Tools Bundle for Eclipse).
Put the Mysql J/Connector JAR into %GlassFish-Tools-Bundle-For-Eclipse-1.2-Install%/glassfishv3/glassfish/lib or %glassfishv3_install%\glassfish\lib. GlassFish already contains Mail, Activation and JMS JARs on its classpath, so there is nothing else to do here.
Using Eclipse, create a new EAR project, as well as a new web module. Once created, right click on the running server and click "add / remove". Add the new EAR so that its deployed, and once deployed, return to the admin console.
You can now set up a virtual server (host) so that you can run multiple domains using the same server (something you might realistically want to do with a prodution server). To do this, you either need to modify a DNS server so that multiple domains resolve to your IP address, or you can modify the hosts file (on windows its c:\windows\system32\drivers\etc\hosts) and add a domain. In the admin console, set up a new virtual server by refering to that domain, and setting its default application to the one you just deployed. That way, any HTTP request which comes in for that domain is sent to your web application. To enable a request like "http://testwsmaxantcouk:8084/" to be redirected to "index.jsp" in your app, you need to set the context root of the webapp to "/", in application.xml or the EAR:
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" id="Application_ID" version="5">
<display-name>Seven
<module>
<web>
<web-uri>SevenWeb.war</web-uri>
<context-root>/</context-root>
</web>
</module>
</application>
In order for this configuration to work, you need to create a database and relevant schema, which you can do using the three SQL files inside the download at the bottom of this article, to create the database, create tables for the realm and application, and insert some initial data into them.
The next step is to add your servlet. I used the eclipse wizard to add a new servlet, which generates a class into your web project with Java EE 6 annotations:
@WebServlet(name = "SevenServlet", urlPatterns = { "/SevenServlet" })
public class SevenServlet extends HttpServlet {
.
.
The servlet delegates its work to a stateless session EJB, which ensures that a transaction is started. The EJB can also be used to check security, although the servlet needs to be deployed within a security context in order to get the web container to automatically reply with a redirection to the login page, in the case where the user is not logged in. In Java EE 6, you do this also using annotations:
@Resource(name="java:app/SevenEJB/EJBSeven")
private EJBSevenLocal ejbLocal;
When the container finds this annotation in an servlet, it injects the EJB automatically.
The EJB is declared with some annotations and resources too:
@DeclareRoles({Roles.REGISTERED})
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class EJBSeven implements EJBSevenLocal {
.
.
This tells the EJB container that the EJB is stateless, uses container managed transactions and may be used by users with roles "registered". The following resources are declared:
@Resource(mappedName = "jms/destinationResource")
private Queue q;
@Resource(mappedName = "jmsConnectionPool")
private QueueConnectionFactory qcf;
@Resource
private SessionContext sessionContext;
@PersistenceContext(unitName="MessageManagement")
private EntityManager em;
These resources are a JMS connection factory, and a queue, for sending the asynchronous message. The session context is the EJBs session context which allows the EJB to query users roles and rollback transactions, amongst other things. A JPA Entity Manager is also declared allowing the EJB to use the database. Finally, the business method declared in the EJB interface is implemented:
@Override
@RolesAllowed({Roles.REGISTERED})
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public String execute(String command) {
.
.
The annotations above tell the container that the user must be in the given role in order to call this method, and that a transaction is required.
Next, the message driven bean is implemented:
@MessageDriven(
activationConfig = { @ActivationConfigProperty(
propertyName = "destinationType", propertyValue = "javax.jms.Queue"
) },
mappedName = "jms/destinationResource")
public class MDBean implements MessageListener {
.
.
The annotations above tell the container which messages should be sent to the onMessage(Message) method of the bean. This bean too has resources, namely a mail session, for sending email:
@Resource(name="mail/Session")
private Session mailSession;
The timer EJB is also implemented:
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class EJBTimerSeven {
@PersistenceContext(unitName="MessageManagement")
private EntityManager em;
.
.
In order for the timer bean to use the entity manager within a transactional context (so that it can for example update or insert data), it too needs a declaration that it uses container managed transactions. The method which is scheduled is defined next:
@Schedule(second="0", minute="*/1", hour="6-23", dayOfWeek="Mon-Fri", dayOfMonth="*", month="*", year="*", info="MyTimer")
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void scheduledTimeout(final Timer t) {
.
.
Note that there is nothing in this beans definition that makes it a timer bean. Rather the method which is to be called when the schedule fires, is simply annotated. I could have just as easily added this method to the main EJB in my project. The schedule pattern is similar to scheduling a cron job in unix, and the second part to ensuring that this methods actions on the entity manager are committed is to add the TransactionAttribute annotation, in this case ensuring that a new transaction is started.
The last thing I needed was a class for JPA, to represent the table I want to write to:
@Entity
public class Message {
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Id
private int uid;
@Column(name="sender")
private String sender;
@Column(name="when_sent")
private Timestamp whenSent;
.
.
This classes annotations show that its a persistable entity using JPA (the @Entity annotation), that the "uid" attribute is the primary key and is generated by the database and that the other attributes map to given columns in the database. Using these very simple annotations and configuring the entity manager using the follwing simple XML, JPA is enabled and it becomes very simple to use indeed.
<persistence>
<persistence-unit name="MessageManagement">
<jta-data-source>jdbc/test</jta-data-source>
<class>uk.co.maxant.test.Message</class>
</persistence-unit>
</persistence>
This XML file is saved as "persistence.xml" and dropped into the META-INF folder of the EJB project.
So, setting up a Java EE 6 enterprise application is really simple! Here are some comments…
Remember that when calling services (EJBs) from your web layer (servlets or in JSF backing beans), that you should always call a façade whose job is to ensure that a transaction is started. In fact, you may have noticed that the realm JDBC resource was configured as a simple SQL Driver, whereas the JDBC resource for the application was set up as an XA Data Source. What’s the difference? Well within the application we used JMS and the application database in the same business method of our EJB, which was run within a transaction. To ensure that either both resources were committed together, or neither, you MUST use XA. The JMS connection factory has a flag on it to say that it should be XA-enabled. The application which you can download at the end of this article contains a hyperlink for starting the process described at the top of the article, as well as another hyperlink for starting it with an error. Any errors that occur in your business logic, before the container attempts to commit resources, are not affected by XA. Rather, XA helps if one of the resources cannot commit, say due to a data constaint defined in the database. That means that if the container had sent the JMS message and then attempted to write data to the database and discovered the data was not valid, it would not commit the sending of the JMS message, so neither resource gets commited! It is important to understand the subtle differences in where the errors occur, in order to see the benefits of XA. For more information see Wikipedia
If you get the following error after deploying the EAR, don’t worry, it’s quite normal: "WEB9051: Error trying to scan the classes at …/eclipseApps/Seven/SevenEJB.jar for annotations in which a ServletContainerInitializer has expressed interest". See here.
GlassFish’s log files can be found at either %glassfishv3_install%/glassfish/domains/domain1/logs if you installed the server, or under %GlassFish-Tools-Bundle-For-Eclipse-1.2-Install&/workspace/.metadata/.plugins/com.sun.enterprise.jst.server.sunappsrv92/domain1/logs if you installed the GlassFish Tools Bundle for Eclipse).
If you experience problems with the security realm, the logs don’t give much information. The easiest solution is to set a breakpoint for exception com.sun.enterprise.security.auth.login.common.LoginException and when it comes up, you can debug the JDBCRealm class. You need to download the source which you can find under https://svn.dev.java.net/svn/glassfish-svn/trunk/v3/security/core (login with user "guest" and no password). More information for the JDBC realm can be found here. One other noteworthy point about realms and GlassFish is that unlike with Tomcat, you must state which realm to use for your web app. You can either do this in the sun-application.xml file, or by adding the realm-name element to your login-config element of web.xml. Both are shown in the download for this article (see bottom).
Until now, I have never used Java EE 6. I had got into the routine of using simple Spring for simple projects. But Java EE 6 is practically the same as Spring in terms of configuring services (EJBs) and adding security and transaction annotations. So I’ve started to ask myself what the advantage of Spring now is. Well Spring still offers simple database handling in terms of not having to worry about exceptions and being able to call Hibernate or other frameworks in a standard way, but JPA offers the same advantages. Spring also offers simplified sending of email, which Java EE 6 does not have, but this is no huge deal. One nice feature of Spring is that it includes AOP, which I have used mostly for improving on Spring Security (by simplifying it, search on this blog for more details). While Java EE 6 doesn’t offer AOP out of the box, you can add it quite simply (see http://www.eclipse.org/aspectj). What’s more, the security offered by Java EE is much more standard than that offered by Spring. To so summarise, it’s getting hard to justify wanting or needing to use Spring. While it is sold as being light weight, modern Java EE app servers are themselves quite light weight, and thanks to "profiles" you can obtain even lighter weight versions of them. See the java.sun.com for more information.
You will notice that the JNDI names used in the resource annotations in my EJBs were added against "mappedName" attributes in the annotations, rather than "name". What that means is that the standard indirection mappings that one should use when deploying EJBs, has not been used, namely the resource "jms/destinationResource" MUST be present in the JNDI tree, rather than the container looking for this name in a mapping in the deployment descriptor. This is fine when deploying your own EJBs to your own app server, but the original idea of creating EJBs was that you would write them, and the deployer would decide how the resources are named, which hence requires a mapping. If you do provice a mapping in the deployment descriptors, then you should use the "name" attribute in the annotations. See here for more information.
The full source code can be downloaded here.
© 2010 Ant Kutschera