Porting seam hotel booking example to OGM

Version 14



    This article documents the process of porting the seam 3 hotel booking example to use Hibernate OGM with MongoDB as the datastore.




    The code is available at: https://github.com/ajf8/seam-booking-ogm


    Note that this will need the parent 'examples' pom and the toplevel seam pom below that for the maven build to work. See below.







    • JBoss Enterprise Application Platform 6 beta. These instructions should work for the community AS 7.
    • I'm using the MongoDB OGM dialect, but these instructions should apply to the infinispan or ehcache backends with some minor changes to the configuration.
    • This is based on the booking example in the seam github at the time of writing. The 3.1.0.Final example had issues.
    • Using Hibernate OGM git master at the time of writing (the mongo dialect has not been released yet).




    • Install MongoDB. No real configuration is required on Fedora - just 'yum install mongodb-server' and 'service mongod start'.


    • Clone the OGM sources, build and install into the local maven repository.


    [alan@ajf-rh jboss]$ git clone https://github.com/hibernate/hibernate-ogm.git

    [alan@ajf-rh jboss]$ cd hibernate-ogm/

    [alan@ajf-rh hibernate-ogm]$ mvn clean install -Pmongodb -s settings-example.xml


    • Extract the EAP (or AS7) distribution:


    [alan@ajf-rh jboss]$ unzip jboss-eap-6.0.0.Beta1.zip


    • Create a directory for the OGM module.


    [alan@ajf-rh jboss]$ cd jboss-eap-6.0/modules/org/hibernate/

    [alan@ajf-rh hibernate]$ mkdir ogm

    [alan@ajf-rh hibernate]$ cd ogm/


    • Copy OGM core, the dialects, and the MongoDB java driver into this module directory.


    [alan@ajf-rh ogm]$ cp ~/jboss/hibernate-ogm/hibernate-ogm-core/target/hibernate-ogm-core-4.0.0-SNAPSHOT.jar .

    [alan@ajf-rh ogm]$ cp ~/jboss/hibernate-ogm/hibernate-ogm-infinispan/target/hibernate-ogm-infinispan-4.0.0-SNAPSHOT.jar .

    [alan@ajf-rh ogm]$ cp ~/jboss/hibernate-ogm/hibernate-ogm-mongodb/target/hibernate-ogm-mongodb-4.0.0-SNAPSHOT.jar .

    [alan@ajf-rh ogm]$ cp ~/.m2/repository/org/mongodb/mongo-java-driver/2.7.2/mongo-java-driver-2.7.2.jar .


    • Create the module.xml (in the same ogm module directory). Ideally we would probably split the dialects into separate modules, but lets keep it simple.





    <?xml version="1.0" encoding="UTF-8"?>

    <module xmlns="urn:jboss:module:1.1" name="org.hibernate" slot="ogm">


            <resource-root path="hibernate-ogm-core-4.0.0-SNAPSHOT.jar"/>

            <resource-root path="hibernate-ogm-mongodb-4.0.0-SNAPSHOT.jar"/>

            <resource-root path="hibernate-ogm-infinispan-4.0.0-SNAPSHOT.jar"/>

            <resource-root path="mongo-java-driver-2.7.2.jar"/>




            <module name="org.jboss.as.jpa.hibernate" slot="4"/>

            <module name="org.hibernate" slot="main" export="true" />

            <module name="javax.api"/>

            <module name="javax.persistence.api"/>

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

            <module name="javax.validation.api"/>

            <module name="org.infinispan"/>

            <module name="org.javassist"/>

            <module name="org.jboss.logging"/>






    • Clone the seam booking example from the seam github. There was an issue with the 3.1.0.Final example at the time of writing. Maybe a future release will be ok.


    [alan@ajf-rh jboss]$ git clone https://github.com/seam/parent.git seam

    [alan@ajf-rh jboss]$ cd seam/

    [alan@ajf-rh seam]$ git clone https://github.com/seam/dist.git

    [alan@ajf-rh seam]$ git clone https://github.com/seam/examples.git

    [alan@ajf-rh seam]$ cd examples/

    [alan@ajf-rh examples]$ cp -r booking/ mongobooking

    [alan@ajf-rh examples]$ cd mongobooking/


    If you don't feel like doing the refactorings and just want to try it out, you should be able to clone the project from my github into the examples directory.


    • Add the OGM, OGM dialect and Hibernate search dependencies to the pom.xml
































    • Create an empty directory for lucene indexes, writable by the application server process:


    [alan@ajf-rh mongobooking]$ mkdir ~/.luceneindexes


    • Update persistence.xml. Note the lucene index directory will need changing.







    <?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_2_0.xsd" version="2.0">

      <persistence-unit name="booking">





          <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>

          <property name="jboss.as.jpa.providerModule" value="org.hibernate:ogm"/>

          <!-- Properties for Hibernate (default provider for JBoss AS) -->

          <property name="hibernate.ogm.datastore.provider" value="org.hibernate.ogm.datastore.mongodb.impl.MongoDBDatastoreProvider"/>

          <property name="hibernate.ogm.mongodb.database" value="booking"/>

          <property name="hibernate.ogm.mongodb.host" value="localhost"/>

          <property name="hibernate.search.default.directory_provider" value="filesystem"/>

          <property name="hibernate.search.default.indexBase" value="/home/alan/.luceneindexes"/>







    • OGM does not currently support criteria queries, which the booking example uses. There are two queries in the example - the action which searches for hotels, and the one which finds a users bookings. Create an eclipse project and then import it to make the code changes.


    [alan@ajf-rh mongobooking]$ mvn eclipse:eclipse



    • Create this class, org.jboss.seam.examples.booking.cdi.FMEMProvider. This allows a FullTextEntityManager to be injected into our action classes.





    package org.jboss.seam.examples.booking.cdi;


    import javax.enterprise.inject.Produces;

    import javax.persistence.EntityManager;

    import javax.persistence.PersistenceContext;


    import org.hibernate.search.jpa.FullTextEntityManager;

    import org.hibernate.search.jpa.Search;



    * @author Emmanuel Bernard


    public class FTEMProvider {


            EntityManager em;



            public FullTextEntityManager getFTEM() {

                    return Search.getFullTextEntityManager( em );





    • We need to add @Indexed annotations to the entities which need indexing by Hibernate Search. @Field on the fields which need indexing, and @IndexedEmbedded to create the embedded index allowing us to search on booking.user.


    Booking entity:








    public class Booking implements Serializable {

        private Long id;


        private User user;



    Hotel entity:





    @Table(name = "hotel")
    public class Hotel implements Serializable {
        private Long id;
        private String name;
        private String address;
        private String city;
        private String state;
        private String zip;
        private String country;



    User entity:






    @Table(name = "traveler")



    public class User implements Serializable {

        private static final long serialVersionUID = -602733026033932730L;


        private String username;



    • Refactor the HotelSearch class to use hibernate search. Start by adding the FullTextEntityManager (which is injected by the FMEMProvider defined earlier using CDI/weld).






    private Provider<FullTextEntityManager> lazyFEM;



    • Replace the queryHotels method with one which uses Hibernate search and lucene. You could experiment with the lucene queries here.



    private void queryHotels(final SearchCriteria criteria) {

        if (criteria.getQuery().length() < 1) {

            nextPageAvailable = false;

            hotels = new ArrayList<Hotel>();




        FullTextEntityManager em = lazyFEM.get();


        final QueryBuilder builder = em.getSearchFactory().buildQueryBuilder()



        final Query luceneQuery = builder.keyword()







        final FullTextQuery query = em.createFullTextQuery(luceneQuery,





        final List<Hotel> results = query




        nextPageAvailable = results.size() > criteria.getPageSize();

        if (nextPageAvailable) {

            // NOTE create new ArrayList since subList creates unserializable

            // list

            hotels = new ArrayList<Hotel>(results.subList(0,


        } else {

            hotels = results;





                .text("Found {0} hotel(s) matching search term [ {1} ] (limit {2})")

                .textParams(hotels.size(), criteria.getQuery(),





    • A similar is refactoring is needed for the BookingHistory class. Add the FullTextEntityManager (as per above), then replace the fetchBookingsForCurrentUser method





    private void fetchBookingsForCurrentUser() {

        String username = currentUserInstance.get().getUsername();

        FullTextEntityManager em = lazyFEM.get();


        final QueryBuilder builder = em.getSearchFactory().buildQueryBuilder()



        final Query luceneQuery = builder.keyword().onField("user.username")





        final FullTextQuery query = em.createFullTextQuery(luceneQuery.





        bookingsForUser = query.getResultList();




    • Build the war and deploy it to the application server.


    [alan@ajf-rh mongobooking]$ mvn clean package

    [alan@ajf-rh mongobooking]$ cp target/seam-booking.war ~/jboss/jboss-eap-6.0/standalone/deployments/


    • Start the application server. I found there are a lot of harmless exceptions caused by CDI/weld, which can be ignored.


    [alan@ajf-rh mongobooking]$ cd ~/jboss/jboss-eap-6.0/bin

    [alan@ajf-rh bin]$ ./standalone.s



    Note that when you search for hotels, unlike the original example, this only matches full keywords (case insensitive). Try typing "hotel" or "marriott"


    • After the application has been started and seed data has been imported, we can see entities in the MongoDB:


    [alan@ajf-rh mongobooking]$ mongo booking
    MongoDB shell version: 2.0.2
    connecting to: booking
    > db.traveler.findOne()
        "_id" : "shane",
        "email" : "shane@example.com",
        "name" : "Shane Bryzak",
        "password" : "brisbane",
        "username" : "shane"