Introduction
TomEE is a lightweight application server which provides a full JEE Web Profile. It provides following technologies within a simple war container:
- CDI - Apache OpenWebBeans
- EJB - Apache OpenEJB
- JPA - Apache OpenJPA
- JSF - Apache MyFaces
- JSP - Apache Tomcat
- JSTL - Apache Tomcat
- JTA - Apache Geronimo Transaction
- Servlet - Apache Tomcat
- Javamail - Apache Geronimo JavaMail
- Bean Validation - Apache BVal
This means you simply can drop your EJBs into a single war and be done with your applications. The same goes for CDI and Entity Beans, all reachable from a single war.
Now to provide an example of such a configuration I have written a 3 tier helloworld which does the basic things an enterprise application performs.
- Display a UI
- Calls a service layer
- Calls the database
- Peforms authentication
- Uses internationalization
And all from within a simple Maven start.
This blogentry is about this program and a step by step guide through it.
About the program and getting started
To get started with the program download the program from Github via http://www.github.com/werpu/tomeemaven.
Once downloaded simply run the program via mvn install tomee:run and then point your browser to
http://localhost:8080/tomeemaven. The rest is very barebones and basic and self explanatory.
You will be brought to a landing page which loads some data over a service layer from the database. Next is a login page which does basic authentication and then brings you into a restricted area.
Now the program itself is as barebones as it gets, the more interesting part is the setup, which this blog will guide you through.
Maven Setup
The barebones maven setup is a simple war file:
<groupId>com.github.werpu</groupId>
<artifactId>tomeemaven</artifactId>
<packaging>war</packaging>
Now to the first interesting part, normally you have in a Maven and Tomcat configuration a huge list of dependencies, which your app uses. Our setup has reduced the dependency list to two entries.
<dependencies>
<!-- contains the entire jee api we only use a subset of it-->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<!-- scope provided means the container delivers it -->
<scope>provided</scope>
</dependency>
<!-- hsqldb references, we start hsqldb as server -->
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
As you can see both dependencies are <scope>provided</scope> which means that the app server provides those two.
The more interesting part is the jee-api include which basically just provides the JEE APIs for the code. We only use a subset of those, but for the simplicity of things a full JEE api include should be enough to get you started.
The second include is the hsql db which we use because we start hsql directly in server mode. (Note TomEE comes with hsql included, we do not have to do anything here regarding including it)
The next part is the TomEE Maven Plugin
<plugin>
<groupId>org.apache.openejb.maven</groupId>
<artifactId>tomee-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<tomeeVersion>1.5.0</tomeeVersion>
<tomeeClassifier>plus</tomeeClassifier>
<debugPort>5005</debugPort>
</configuration>
</plugin>
This basically is our TomEE starter, we de not have to do anything about its configuration. With this entry our mvn clean install tomee:run is possible and our war automatically picked up as application war.
The last part is the compile time instrumentation weaving for OpenJPA which enhances our entities at compile time.
<plugin>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-maven-plugin</artifactId>
<version>2.2.0</version>
<executions>
<execution>
<id>mappingtool</id>
<phase>process-classes</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
com/github/werpu/tomeedemo/orm/*
</includes>
</configuration>
</plugin>
This is basically the entire maven setup.
File structure
The folder hierarchy follows a strict Maven setup with small enhancements for the TomEE Maven plugin.
It is as follows:
Following Folders are of interest
- src/main/java The Java sources
- src/main/resources the resources folger hosting the beans and persistence.xml, this folder later will be compiled into the web-inf/classes folder along with the java sources
- src/main/webapp the web application folder
- src/main/tomee hosting the TomEE configuration file overrides (we will come later to this important folder)
The main difference to a standard Maven layout is the src/main/tomee folder.
It currently hosts 2 files:
- tomcat-users.xml
- and tomee.xml
tomee.xml hosts a new database resource which connects to a locally running HSQL DB Server. Outside of that it is a blank copy of what is provided from the standard TomEE distribution.
tomcat-users.xml is the standard Tomcat UserDatabase Realm but provides one additional user, who is the login user for our secured part.
We will go later into more details regarding both files. The important part about this files is, that they replace the original files during the build process and bundle them into the running TomEE once you issue mvn install tomee:run on the command line.
That way you can override any of the standard settings of TomEE with your custom settings.
3 Tiers
The UI layer
Here we use plain JSF as provided by TomEE (in our case Apache MyFaces 2.1.9 which as time of writing is almost the most recent distribution of Apache MyFaces)
The main difference is that we basically use CDI instead of JSF managed beans. Here is an example page controller class implemented as CDI bean.
@Named
@RequestScoped
public class HelloView
{
@EJB
HelloEJB helloEjb;
//---------------------------- getter and setter ---------------------------------
public String getHelloWorld()
{
return helloEjb.getHello();
}
}
The main difference here to a standard Tomcat setup is, that we can inject an EJB directly into our managed bean. This is thanks to TomEE.
The EJB can be straightly bundled into the WAR file, no need for EAR and complicated classloader hierarchies.
This brings us directly to our second layer.
The service/DAO layer
We have implemented the Service layer as stateless Session Bean, since we use JPA we will omit separate DAOs in favor of simplicity.
The Service does not do to much it basically just writes to the database in case no data is present otherwise it will read from the database.
@Stateless
public class HelloEJB
{
@PersistenceContext(name = "Demo_Unit")
EntityManager em;
public String getHello()
{
Query query = em.createQuery("select hello from HelloEntity hello");
if (query.getResultList().size() == 0)
{
HelloEntity entity = new HelloEntity();
em.persist(entity);
return entity.getHelloWorld();
}
HelloEntity entity = (HelloEntity) query.getResultList().get(0);
return entity.getHelloWorld();
}
}
This brings us straight to
The database layer
Centralized database connection
The database layer is a little bit more complicated because it needs several configuration files.
First we have to deal with the a centralized managed database connection.
TomEE utilizes a resource entry in tomee.xml to enable the centralized database connection. (Which theoretically can be shared over multiple applications)
Here is our entry:
<Resource id="MyDataSource" type="DataSource">
JdbcDriver org.hsqldb.jdbcDriver
JdbcUrl jdbc:hsqldb:hsql://localhost:9001/testdb2
UserName sa
Password
JtaManaged true
</Resource>
What is strikingly odd here is, that this is not the same syntax as used in a standard context.xml Tomcat configuration but instead TomEE follows its own syntax which is explained more thoroughly here (I have not yet tested if a context.xml db connection also works, given that there is the web.xml method, I probably never will (see below)).
In our case we connect to a running HSQLDB server which is started with the application automatically (we will come later to that)
The important parts are following
- the Datasource id="MyDataSource"
- the Type being a DataSource: type="DataSource"
- and JtaManaged being true
The rest is standard JDBC.
The main advantage of this method is mostly the centralized connection which can be shared over multiple web applications, and also that it allows for more configuration options compared to the web.xml method. The downside however is, that it is centrally managed which means you have to alter your config entries in the tomee dir. In our case the project does that automatically during deployment of the web application.
Web application managed database connections
As one of the readers of this blog has pointed out, the method mentioned before is centralized but non standard (not JEE), which means for a real deployment you have to change the configuration of your tomee and to the worse if you go to a different app server you have to setup the connection again for that server, in some cases this is not wanted. Since we use Servlet 3.0 there is a standard way to define database connections on a per webapp base. We can achieve it simply by adding following code to our web.xml.
<data-source>
<name>MyDataSource</name>
<class-name>org.hsqldb.jdbcDriver</class-name>
<url>jdbc:hsqldb:hsql://localhost:9001/testdb2</url>
<user>sa</user>
<password></password>
</data-source>
The advantage of this method is simplicity and you do not tamper with the config directory of Tomee, the downside is, that MyDataSource only can be used within the context of the web application and you will lose some advanced configuration options the centralized method can provide.
Persistence.xml
Now to the second part the persistence.xml. As already discussed we use OpenJPA for persistence, because it already is present in the container. Hibernate or EclipseLink also would be possible by adjusting the pom.xml and removing the OpenJPA implementation and adding the other jars. Following page has an example which exactly does that.
For our persistence.xml we use following entries:
<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">
<!-- simply all annotated persistent entities will be part of this unit-->
<persistence-unit name="Demo_Unit">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>MyDataSource</jta-data-source>
<class>com.github.werpu.tomeedemo.orm.HelloEntity</class>
<properties>
<property name="openjpa.jdbc.DBDictionary" value="hsql"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
<!-- disable runtime instrumentation -->
<property name="openjpa.DynamicEnhancementAgent" value="false"/>
<property name="openjpa.Log" value="DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SQL=TRACE"/>
<!-- with those two settings openjpa behaves better for merge and detach -->
<property name="openjpa.DetachState" value="loaded(DetachedStateField=true)"/>
<property name="openjpa.Compatibility" value="IgnoreDetachedStateFieldForProxySerialization=true"/>
<property name="openjpa.jdbc.MappingDefaults"
value="ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict"/>
</properties>
</persistence-unit>
</persistence>
The important parts are following:
The rest are internal openjpa settings, like foreign key handling, that we use compile time instrumentation instead of runtime instrumentation (see also the maven config for that one), or a helper which preserves the states in serialized scenarii, to help to speed up subsequent merges. Neither of that is if importance for the blog.
Now last but not least our entity class which is used by our service:
@Entity
public class HelloEntity
{
@Id
@GeneratedValue
Integer id;
@Column(name = "HELLO_WORLD")
String helloWorld = "helloWorld from entity";
@Version
protected Integer optlock;
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getHelloWorld()
{
return helloWorld;
}
public void setHelloWorld(String helloWorld)
{
this.helloWorld = helloWorld;
}
public Integer getOptlock()
{
return optlock;
}
public void setOptlock(Integer optlock)
{
this.optlock = optlock;
}
}
Authentication
We use standard server hosted authentication for our application to limit user access to certain pages. Deeper knowledge on the mechanisms of this authentication method is outside of the scope of this blog, but you can find information here.
The application has a secured page which can be accessed from our landing page. For simplicty reasons we use the standard tomcat-users.xml file and the UserDatabase realm defined by tomcat as standard.
All we do is to add another user and a new permission to the realm:
<role name="authenticated-user" />
<!-- we use a new user in our standard realm for the authenticated pages -->
<user name="test" password="user" roles="authenticated-user" />
The web.xml now has to have following entries to secure the page:
<!--
authentication , we use browser authentication
and the tomcat-users.xml file as authentication realm
-->
<security-constraint>
<web-resource-collection>
<web-resource-name>Authenticated Pages</web-resource-name>
<url-pattern>/user/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>authenticated-user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>UserDatabase</realm-name>
<form-login-config>
<form-login-page>/login/login.jsf</form-login-page>
<form-error-page>/login/error.jsf</form-error-page>
</form-login-config>
</login-config>
What we do here is to secure all pages under /user/* only accessible for users with the role authenticated-user which was defined before.
<realm-name>UserDatabase</realm-name>
was the standard realm which we defined our new user and new role
We also define a login and error page for a standard login.
The login looks as follows:
<form method="POST" action="j_security_check">
<input type="text" name="j_username"/>
<br />
<input type="password" name="j_password"/>
<br />
<input type="submit" value="Submit"></input>
<input type="reset" value="Reset"></input>
</form>
This is a standard login form for server based authentication, it has to follow a certain naming syntax, hence, the non JSF syntax.
The Database
We could make it simpler by running our database in embedded mode, which would look like following:
<Resource id="MyDataSource" type="DataSource">
JdbcDriver org.hsqldb.jdbcDriver
JdbcUrl jdbc:hsqldb:file:data/hsqldb/testdb
UserName sa
Password
JtaManaged true
</Resource>
This however has the downside of not being able to connect to the database with third party tools, while the db is running. While simplicty is the primary objective of the demo project, which this blog is based upon we stray away from this path in case of the DB. Our primary aim is to have a db which runs in server mode. Normally this would be an Oracle, MySQL or PostgresSQL db. However in our case we simply want the db to start once we start our own server.
For this we use a small trick, we use a servlet to start the db. (theoretically a context listener also would suffice)
The servlet has to be started via loadonstartup = 1
@WebServlet(urlPatterns = "/db/*", loadOnStartup = 1)
public class HSQLDBStartupServlet extends HttpServlet
{
Logger log = Logger.getLogger(HSQLDBStartupServlet.class.getName());
@Override
public void init() throws ServletException
{
super.init();
try
{
log.info("Starting Database");
HsqlProperties p = new HsqlProperties();
p.setProperty("server.database.0", "file:data/hsqldb");
p.setProperty("server.dbname.0", "testdb2");
p.setProperty("server.port", "9001");
Server server = new Server();
server.setProperties(p);
server.setLogWriter(null);
server.setErrWriter(null);
server.start();
}
catch (ServerAcl.AclFormatException afex)
{
throw new ServletException(afex);
}
catch (IOException ioex)
{
throw new ServletException(ioex);
}
}
}
And the entry in the tomee.xml looks like following:
<Resource id="MyDataSource" type="DataSource">
JdbcDriver org.hsqldb.jdbcDriver
JdbcUrl jdbc:hsqldb:hsql://localhost:9001/testdb2
UserName sa
Password
JtaManaged true
</Resource>
Almost the same as before however the JDBCURL parameter is different it now uses a TCP/IP connection instead of direct file access.
Summary
This sums up our little walkthrough through the demo application. Have in mind, that the application is a good kickstarter for your own projects, however in a real production environment some adjustments have to be made. For instance authentication needs to be set towards a database, passwords need to be replaced by their hash sums, authentication has to go through https, and also the db should not be generated out of the entities.
But all of this would sacrifice the simplicity of the demo project. Also feel free to comment or fork the project for your own purposes or improvements. Any participation is welcome as always in opensource projects.
Links
- TomEE Maven project
- Apache TomEE project
- OpenJpa project
- Mark Strubergs blog (JPA and CDI information)
- Oracle server side authentication tutorial
- How to start HSQLDB in a webapp