In my previous post we setup a Maven/Eclipse project for developing RESTful web applications on JBoss AS 7. A RESTful web service that is not using a database is some what of an oddity. Therefor in this blog we'll extend the project with JPA.

Configuration: Adding a JBoss Module

I want to use Postgres as the underlying database. To install the required JDBC driver for Postgres in the JBoss AS 7 container, we need to add a module. As I mentioned in the previous post, the module structure of JBoss is reflected on the file system.

The files in the ''main'' directory that constitute the module are the folloing:

  • module.xml describes the module. The content follows below.
  • postgresql-9.0-801.jdbc4.jar is the postgres JDBC4 driver.
  • postgresql-9.0-801.jdbc4.jar.index is an index file generated by JBoss Annotation Indexer when the modules are scanned.

So in order to add a module to the JBoss container, we need to write an XML file:

<module xmlns="urn:jboss:module:1.0" name="org.postgres">
  <resources>
    <resource-root path="postgresql-9.0-801.jdbc4.jar"/>
  </resources>

   <dependencies>
     <module name="javax.api"/>
     <module name="javax.transaction.api"/>
   </dependencies>
</module>

Now we also need to enlist the datasource in the standalone.xml: the main configuration for JBoss. You'll find it in the ''standalone/configuration'' subdirectory of the JBoss installation. The file already has a datasource and driver for h2. So we add a new node:

<?xml version='1.0' encoding='UTF-8'?>
<server name="brandhout.local" xmlns="urn:jboss:domain:1.0">
...
            <datasources>
                <datasource>
...
                </datasource>
                <drivers>
                    <driver name="postgres" module="org.postgres">
                        <xa-datasource-class>
                            org.postgresql.xa.PGXADataSource
                        </xa-datasource-class>
                    </driver>
                    <driver name="h2" module="com.h2database.h2">
                        <xa-datasource-class>
                            org.h2.jdbcx.JdbcDataSource
                        </xa-datasource-class>
                    </driver>
                </drivers>
            </datasources>
...
</server>

Now, after restarting the server, we can finish the configuration using the admin console.

The ''New Datasource'' button will open up a dialog that will guide you through a few simple steps to configure the datasource.
The changes will be persisted in the standalone.xml, which will now contain a fragment like this:

..
                <datasource jndi-name="LibraryDS" pool-name="LibraryDS_Pool" enabled="true" jta="true" use-java-context="true" use-ccm="true">
                    <connection-url>
                        jdbc:postgresql://localhost:5432/library
                    </connection-url>
                    <driver>
                        postgres
                    </driver>
                    <security>
                        <user-name>
                            library
                        </user-name>
                        <password>
                            library
                        </password>
                    </security>
                </datasource>
...

Reading from the database

Now we need to configure our application to read from the database. To configure the database as a persistence context in JPA, we add a persistence.xml to our project.

<?xml version="1.0" encoding="UTF-8" ?>
<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_1_0.xsd"
	version="1.0">

	<persistence-unit name="library" transaction-type="JTA">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:/LibraryDS</jta-data-source>
		<properties>
			<property name="hibernate.hbm2ddl.auto" value="create-drop" />
		</properties>
	</persistence-unit>

</persistence>

The java code we have to write is quite trivial JPA code mixed in with some JAX-RS annotation. For completeness they are included here below (collapsed).

package com.xebia.library.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.ws.rs.FormParam;

@Entity
@SequenceGenerator(name="BOOK_SEQ", sequenceName="BOOK_SEQ")
public class Book {

	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="BOOK_SEQ")
	@FormParam("id")
	private Long id;
	
	@FormParam("title")
	private String title;
	
	public Long getId() {
		return id;
	}

	public String getTitle() {
		return title;
	}
}
package com.xebia.library;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;

import java.util.List;

import javax.ejb.Local;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.jboss.resteasy.annotations.Form;

import com.xebia.library.model.Book;

@Local
@Path("books")
@Produces(APPLICATION_JSON)
public interface BookRepository {
	@GET
	@Path("/")
	List<Book> all();

	@POST
	@Path("/")
	Book create(@Form Book entity);

	@GET
	@Path("/{id}/")
	Book getById(@PathParam("id") long id);

	@PUT
	@Path("/{id}/")
	Book update(@Form Book entity);

	@DELETE
	@Path("/{id}/")
	void remove(@PathParam("id") long id);
}
package com.xebia.library.impl;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import com.xebia.library.BookRepository;
import com.xebia.library.model.Book;

@Stateless
public class BookRepositoryBean implements BookRepository {

	@PersistenceContext
	private EntityManager em;

	@Override
	public List<Book> all() {
		CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<Book> query = builder.createQuery(Book.class);
		Root<Book> root = query.from(Book.class);
		query.orderBy(builder.asc(root.get("id")));
		return em.createQuery(query).getResultList();
	}

	@Override
	public Book create(Book book) {
		em.persist(book);
		return book;
	}

	@Override
	public Book byId(long id) {
		return em.find(Book.class, id);
	}

	@Override
	public Book update(long id, Book book) {
		return em.merge(book);
	}

	@Override
	public void delete(long id) {
		em.remove(byId(id));
	}

}

A few things to notice:

  • The JPA entity is also used as a ''backing'' object for Form posts. The @FormParam annotation is used to parameters posted to the fields of the object.
  • The Repository is now also used as a local interface for the stateless bean.

After deploying the application to the server, we can test it using this simple tool.

Conclusion

With just a few classes we were able to deploy a RESTful web service that enables users to maintain a (rudimentary) book list. The WAR that we build is really tiny: It only contains a few classes. The application relies on JEE specifications that are implemented in the JBoss container. A few remarks about this:

  1. Developing a JEE application becomes much simpler and more fun! It feels almost like RAD web development alla Grails.
  2. Testing an application like this is non-trivial: Much of the functionality is provided by the container. With a 'thicker' maven project with Hibernate and Spring/Seam as dependencies, we could mimic (parts of) the deployed application in an in-build testing environment. How do we do this now? One possible answer is to use Arquillian. A future blog will have to cover that...

Of course the application we have now is far from finished. To start with, it has no user interface...