Tuesday, January 3, 2012

JBoss 7: creating a prototype webapp

Introduction

When I do a project, I do so using the "Scrum" methodology. Without going into details on what that exactly is, it means that I (or actually the whole team I'm part of) develop applications in iterations of 2 weeks each. At the end of each iteration I deliver something that is perhaps not complete, but it does work (at least that's the idea). The intent is to not have something that could be put in production, but something you can demonstrate.

When I have to do a web module, I generally do not start developing against a 'real' database like Oracle or MySQL. At that point in development the database design is probably still very much alive and will change from moment to moment; when you write application code you want the interface (which includes the database schema) to be nice and stable, or else you'll be modifying your code every time someone else in the team decides that things need to be totally different, probably with good reason.

But I still want to create something that works and can be demonstrated, so I need to be a little creative. Enter: embedded pure Java databases. They allow you to create a real database driven web module, without needing an actual physical database running anywhere. This makes them perfect to create prototypes, or to do that "iteration 1" web module.

Note that I assume you read the Jboss 7 getting started article before you read this one.


Choice of database

There are three basic pure Java database implementations that you could use.

Derby
Don't know much about it myself, but it is bundled with the JDK. I hope you use Maven, so the fact that it is bundled with the JDK should not be a reason to want to use it. The one thing I do know is that it doesn't perform very well compared to its two bigger brothers.

H2
A popular DBMS that even has a place in a 'real' application environment, especially because H2 has an XA datasource available but can also adhere to real life requirements like database encryption and SSL support. It is basically the good old HSQL with a feature set that allows you to use it in a production environment. If you want to do that, by all means use H2.

HSQL
My DBMS of choice for prototype applications and unit test environments. Probably because I learned to trick of the trade using it :) Since we'll be using JPA there isn't much of a difference, if you prefer H2 then by all means use it. A big advantage is of course that JBoss 7 comes with H2 already pre-installed and an example datasource is already setup, you have almost no work.


Setup

You don't need to do much to setup an in-memory HSQL database. Of course you need to install it as a module in JBoss 7. You do this by downloading the latest HSQL version (which is a JDBC 4 compliant driver). Create a directory modules/org/hsqldb/main. Copy the module.xml of the org.h2database.h2 module to your newly created directory. Put the HSQL jar you downloaded into this directory as well. Then modify the module.xml to the following:

<module xmlns="urn:jboss:module:1.0" name="org.hsqldb">
  <resources>
    <resource-root path="hsqldb-2.2.6.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>

Assuming the version you downloaded is 2.2.6; put whatever jar name you have.

Now we need to create the datasource. Open up standalone/configuration/standalone.xml and locate the drivers. Add the following driver:

<drivers>
  <driver name="hsqldb" module="org.hsqldb"/>
  ...
</drivers>

And add a datasource that is going to form our database:

<datasource jndi-name="TestappDS" pool-name="TESTAPPDS" enabled="true" jta="true" use-java-context="true" use-ccm="true">
  <connection-url>
    jdbc:hsqldb:mem:testapp
  </connection-url>
  <driver>
    hsqldb
  </driver>
  <pool>
    <prefill>false</prefill>
    <use-strict-min>false</use-strict-min>
    <flush-strategy>FailingConnectionOnly</flush-strategy>
  </pool>
  <security>
    <user-name>
      sa
    </user-name>
    <password>
    </password>
  </security>
</datasource>

Basically the H2 datasource but with a different url and password. Webapps generally do simple database transactions so we'll use a plain old local-tx datasource but we do enable JTA (if only because that works the best in combination with Hibernate 4). The url as you can find in the HSQL user manual will setup a database that exists purely in memory only. It is created when the database starts and it will be destroyed when the database (or JBoss) shuts down.

This has the advantage that whenever you reboot your server the database will be fresh again which is precisely what you want in a prototype or 'demonstration' iteration. Of course the main disadvantage is that you'll start with an empty database which is pretty much useless; we'll have to add a way of bootstrapping the database with some starting data. More on that later.


The persistence.xml

In order to properly work with an in-memory database we still need a way to actually create the database itself. Fortunately this task can be automated. Since we will be using Hibernate 4 we can use its auto-DDL generation features. We can set it up like this:

<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">
   <persistence-unit name="testapp" transaction-type="JTA">
      <jta-data-source>java:/TestappDS</jta-data-source>
      <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
        <property name="hibernate.hbm2ddl.auto" value="update"/>
        <property name="hibernate.format_sql" value="false" />
        <property name="hibernate.show_sql" value="false" />
      </properties>
   </persistence-unit>
</persistence>

'update' generation means create if it isn't there yet, or modify if something already exists. Of course our database being in-memory this means that the entire database will simply be created; I still use update because it gives more sane logging output than 'create-drop'.

Note the datasource JNDI name; the fact that the DS lives in the java context is because of the use-java-context="true" attribute of the datasource.

If you are not that familiar with Hibernate you may be wondering what will be used to generate the schema from? The answer is simple: from the JPA entities.

Adding some entities


To see Hibernate and HSQL in action we need some JPA entities. Create them wherever you want, in my case in a WAR I'll call testapp-web.war. As an example lets create a User entity which is bound to a UserGroup.

@Entity
@Table(name="app_user")
public class User implements Serializable {

  @Id
  @GeneratedValue
  private long id;
  
  private String name;
  private String username;
  private String password;
  private String email;
  
  private boolean admin;

  @ManyToOne
  private UserGroup group;
  
  public User(){
    
  }
  
  public User(String name, String username, String password, UserGroup group, boolean admin){
    this.name = name;
    this.username = username;
    this.password = password;
    this.group = group;
    this.admin = admin;
  }

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  public UserGroup getGroup() {
    return group;
  }

  public void setGroup(UserGroup group) {
    this.group = group;
  }

  public boolean isAdmin() {
    return admin;
  }

  public void setAdmin(boolean admin) {
    this.admin = admin;
  }
}

@Entity
public class UserGroup implements Serializable {

  @Id
  @GeneratedValue
  private long id;
  private String name;
  
  public UserGroup(){
    
  }
  
  public UserGroup(String name){
    this.name = name;
  }

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}


Two simple entities with some properties and a 1:N relationship between the two. As you can see I kept the JPA annotations sparse; that is a rule of thumb when creating your prototype webapp; use as little JPA annotations as possible. The reason is simple: you have no guarantee yet what the end database is going to be at this point, any configuration items you might add are likely to need changing later on. Don't do it, the defaults chosen by Hibernate are more than adequate for your prototype. Design first, fill in the details when you know them for sure.

You may notice the table annotation on the User to alter the table name; this is simply because I have the habit of avoiding overly common identifier names. 'user' is just such an identifier. You could also name the entity AppUser of course, that's up to you.

At this point you can publish the application and start the server. If you did everything correctly you'll notice that Hibernate creates the two tables for you in the startup logging.


Bootstrapping the database

A web application is actually quite a difficult application to work with as it has a very muddy lifecycle. It doesn't have a clearly defined start point and end point; there is no main().

But what it does have is a servlet context which is at one point initialized. That is what we can treat as our main() and it makes a good point to bootstrap our database. In order to do so, we need to create a WebListener.


@WebListener
public class DbContextListener implements ServletContextListener {

  private static boolean initialized = false;
  private static Boolean lock = Boolean.FALSE;
  
  @Override
  public void contextDestroyed(ServletContextEvent ev) {
    
  }

  @Override
  public void contextInitialized(ServletContextEvent ev) {
    
    synchronized(lock){
      
      if(initialized == false){
        initialized = true;
        
        BootstrapDao bsDao = lookupBootstrapDao();
        bsDao.persistTestData();
      }
    }
  }

  private BootstrapDao lookupBootstrapDao(){
    
    try{
      InitialContext ic = new InitialContext();
      return (BootstrapDao) ic.lookup("java:/global/testapp-web/BootstrapDao");
    } catch(Throwable t){
    }
  }
}

BootstrapDao is a simple DAO EJB which will persist the data for us. We'll get to it in a moment. Normally I would inject the EJB using @EJB of course but this gives me a chance to demonstrate looking up a local EJB resource under JEE6 / EJB 3.1. I deploy my EJB in a war module called testapp-web.war that is not part of an EAR, hence the JNDI name.

To make it more complete, here are other conventions. In these conventions assume that the following is the case:

EAR name: myapp.ear
WAR name: testapp-web.war
Bean name: TestBean
Local interface: myapp.TestBeanLocal
Remote interface: myapp.TestBeanRemote

CaseJNDI name
Only war, no interfacejava:global/testapp-web/TestBean
Ear, war, no interfacejava:global/myapp/testapp-web/TestBean
Ear, war, both interfaces, local interface lookupjava:global/myapp/testapp-web/TestBean!myapp.TestBeanLocal
Ear, war, both interfaces, remote interface lookupjava:global/myapp/testapp-web/TestBean!myapp.TestBeanRemote


This list has two basic patterns.

- short notation, which is the JNDI name without the business interface part (!package.InterfaceClass). This JNDI name will be created if you have no business interface or only one.
- the full notation, which includes the business interface. Which one (local/remote) exists depends on if you have business interfaces bound to your EJB of course.

These bindings are according to the JEE6 specification; they should work and be the same on all JEE6 complaint servers (Glassfish 3+, JBoss 6+, Weblogic 12+, Websphere 8+, Geronimo 3+). If you keep the loglevel on INFO, JBoss 7 will actually log all the possible JNDI names of any given EJB in your project. You can also lookup the JNDI context through the server management interface though.

The @WebListener annotation is a new feature of the JEE6 specification. You no longer need to define such resources in the web.xml, oh glory. The same for filters (@WebFilter) and servlets (@WebServlet).

In any case, the code above also protects against the context being initialized multiple times for whatever reason. The database only needs to be initialized once.


Adding bootstrap data

This unfortunately is the (really) boring part of the job. To insert data you must do boring inserts. To do boring inserts in a pure Java database with entity relationships, the easiest way is to simply create objects and persist them. For that we need an active transaction, hence we do it in a DAO EJB.

public void persistTestData(){
  
  UserGroup group = new UserGroup("myusergroup");
  em.persist(group);

  User user1 = new User("gimby", "username", "password", group, "gimby@somewhere.cm", true);

  User user2 = new User("someone", "username", "password", group, "someone@somewhere.cm", false);

  em.persist(user1);
  em.persist(user2);

  // etc. etc.
}

As said, boring code. But you'll only need to maintain it for as long as you have your prototype setup, it won't haunt you forever. Given that fact I would just bite the bullet and do it the simple way.

The alternative is of course to be able to define your data in some simple format and let a piece of code stream it into your database. I like the way how Play framework does it for example; you define your test data objects in YAML format and the Play persistence layer can persist it all for you, including the entity relationships. But Play has a really nasty piece of code that wraps around Hibernate and JPA to be able to do that (as well as defining some hard conventions that make it possible for the code to make safe assumptions), you cannot easily get that done through plain old JPA code and a YAML parser.



In closing


A rather short article that if you look at it is quite simplistic. Lets review what the article describes:

- how to create a persistence unit around an in-memory database
- how to use a WebListener to bootstrap the database
- how to lookup an EJB under JEE6 without using injections

I'm not here to move mountains, sometimes having a pile of simple facts documented in a straightforward way can help to see the possibilities you would normally ignore. For example, given how the code is structured it shouldn't be too hard to use the exact same code to setup a unit test environment for your application, don't you think?

No comments:

Post a Comment