GlassFish 3 In 30 Minutes
Join the DZone community and get the full member experience.
Join For FreeThe 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.
From http://blog.maxant.co.uk/pebble/2010/05/08/1273354800000.html
Opinions expressed by DZone contributors are their own.
Comments