Dienstag, 27. November 2012

TomEE and Maven

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:

Folder Hierarchy

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.

Override Config Files

 

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

  1. the Datasource id="MyDataSource"
  2. the Type being a DataSource: type="DataSource"
  3. 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:

  •  <persistence-unit name="Demo_Unit"which provides us with the persistence unit name used by our EJB.
  • <jta-data-source>MyDataSource</jta-data-source> As datasource definition.
  • <class>com.github.werpu.tomeedemo.orm.HelloEntity</class> For our enity class

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 

  1. TomEE Maven project
  2. Apache TomEE project
  3. OpenJpa project
  4. Mark Strubergs blog (JPA and CDI information)
  5. Oracle server side authentication tutorial
  6. How to start HSQLDB in a webapp 

 

 

 

 

9 Kommentare:

  1. Just wondering, for the database layer you used TomEE's proprietary configuration file instead of the standard Java EE equivalent.

    Since the post otherwise does a great job of demonstrating the various Java EE techniques and how well they work in TomEE, I'm really curious why you choose not to use the Java EE version here (i.e. the data-source element in web.xml, see my post at http://henk53.wordpress.com/2012/04/15/jsf2-primefaces3-ejb3-jpa2-integration-project step 12 for some details)

    Anyway, great post and thanks for sharing this :)

    AntwortenLöschen
    Antworten
    1. Hello thanks for the excellent link, the reason why I went with the TomEE configuration was that I wanted to have the datasource to be container managed instead of application managed. Thats all there is to it.

      Löschen
    2. I will add the info on how to do it application managed later today, it makes sense to have it both ways as information.

      Löschen
    3. Vielen dank :)

      If I'm not mistaken, then defining a data-source element in web.xml (or optionally via @DataSource on a Servlet or EJB), constitutes a container managed data source just as well.

      Under the covers an implementation should do approximately the same. I know that at least JBoss AS in one of it latest versions (7.1.2, 7.1.3, both not directly downloadable), does do exactly the same.

      As such, it 'should' just be a matter of using proprietary syntax vs standardized syntax.

      The proprietary syntax does often allow for more advanced things to be setup. For instance, JBoss AS has an HA option where you can configure multiple URLs to your databases and if one of them fails, the other is used.

      Löschen
    4. p.s.

      To clarify "container managed"; what I meant with that is that the container eventually instantiates the source and takes care of connection pooling, etc.

      "application managed" would be when the user just instantiates a datasource using the new operator in Java SE style.

      Then parallel to this there would be the concept of an "embedded data source" (defined inside the .war or .ear) and an "external data source" (defined inside the server).

      With the proprietary syntax we often think of an external data source, but these can be embedded as well. At least GlassFish, JBoss, WebLogic and Tomcat (context.xml) can do this. I'm not sure about tomee.xml.

      The standardized syntax can only be used for embedded purposes. I'm not 100% sure about what terminology the spec actually uses, but I guess it would be along the lines of an "embedded standardized container manager data source" :P

      Löschen
    5. Ok thanks for the thorough explanation, I altered the docs accordingly.

      Löschen
  2. I wish there were a maven artifact just like this

    AntwortenLöschen
    Antworten
    1. Hi, I cannot provide such an artifact, but feel free to clone the project as the kickstarter for your own projects.

      Löschen
  3. Hi:

    Thank you so much for this post, the sample application, and the instructions on using maven. Codenvy (a cloud IDE) has ported our Factory capability to work with this sample. It will launch an IDE with the configuration of this sample app, and it can be built and run without any additional elements being configured.

    Here is the URL that will launch the environment. We'd appreciate some inputs on how we can make it better. We had to add some JSF dependencies to the maven pom.xml otherwise it worked ok.

    https://codenvy.com/factory?v=1.0&pname=tomeemaven&wname=tylerjewell&vcs=git&vcsurl=http%3A%2F%2Fcodenvy.com%2Fgit%2Ff8%2Fba%2F32%2Fworkspace3kr62i8x4nkse6pm%2Ftomeemaven&idcommit=f0688e2c437174b0719f368c79be0bf927d27648&action=openproject&ptype=War

    AntwortenLöschen