Tuesday Apr 10, 2012

Java EE 6 and NoSQL/MongoDB on GlassFish using JPA and EclipseLink 2.4 (TOTD #175)


TOTD #166 explained how to use MongoDB in your Java EE 6 applications. The code in that tip used the APIs exposed by the MongoDB Java driver and so requires you to learn a new API. However if you are building Java EE 6 applications then you are already familiar with Java Persistence API (JPA). Eclipse Link 2.4, scheduled to release as part of Eclipse Juno, provides support for NoSQL databases by mapping a JPA entity to a document. Their wiki provides complete explanation of how the mapping is done.

This Tip Of The Day (TOTD) will show how you can leverage that support in your Java EE 6 applications deployed on GlassFish 3.1.2.

Before we dig into the code, here are the key concepts ...
  • A POJO is mapped to a NoSQL data source using @NoSQL or <no-sql> element in "orm.xml".
  • A subset of JPQL and Criteria query are supported, based upon the underlying data store
  • Connection properties are defined in "persistence.xml"
Now, lets lets take a look at the code ...
  1. Download the latest EclipseLink 2.4 Nightly Bundle. There is a Installer, Source, and Bundle - make sure to download the Bundle link (20120410) and unzip.
  2. Download GlassFish 3.1.2 zip and unzip.
  3. Install the Eclipse Link 2.4 JARs in GlassFish
    • Remove the following JARs from "glassfish/modules":
      org.eclipse.persistence.antlr.jar
      org.eclipse.persistence.asm.jar
      org.eclipse.persistence.core.jar
      org.eclipse.persistence.jpa.jar
      org.eclipse.persistence.jpa.modelgen.jar
      org.eclipse.persistence.moxy.jar
      org.eclipse.persistence.oracle.jar
    • Add the following JARs from Eclipse Link 2.4 nightly build to "glassfish/modules":
      org.eclipse.persistence.antlr_3.2.0.v201107111232.jar
      org.eclipse.persistence.asm_3.3.1.v201107111215.jar
      org.eclipse.persistence.core.jpql_2.4.0.v20120407-r11132.jar
      org.eclipse.persistence.core_2.4.0.v20120407-r11132.jar
      org.eclipse.persistence.jpa.jpql_2.0.0.v20120407-r11132.jar
      org.eclipse.persistence.jpa.modelgen_2.4.0.v20120407-r11132.jar
      org.eclipse.persistence.jpa_2.4.0.v20120407-r11132.jar
      org.eclipse.persistence.moxy_2.4.0.v20120407-r11132.jar
      org.eclipse.persistence.nosql_2.4.0.v20120407-r11132.jar
      org.eclipse.persistence.oracle_2.4.0.v20120407-r11132.jar
  4. Start MongoDB
    1. Download latest MongoDB from here (2.0.4 as of this writing).
    2. Create the default data directory for MongoDB as:
      sudo mkdir -p /data/db/
      sudo chown `id -u` /data/db
      Refer to Quickstart for more details.
    3. Start MongoDB as:
      arungup-mac:mongodb-osx-x86_64-2.0.4 <arungup> ->./bin/mongod
      ./bin/mongod --help for help and startup options
      Mon Apr  9 12:56:02 [initandlisten] MongoDB starting : pid=3124 port=27017 dbpath=/data/db/ 64-bit host=arungup-mac.local
      Mon Apr  9 12:56:02 [initandlisten] db version v2.0.4, pdfile version 4.5
      Mon Apr  9 12:56:02 [initandlisten] git version: 329f3c47fe8136c03392c8f0e548506cb21f8ebf
      Mon Apr  9 12:56:02 [initandlisten] build info: Darwin erh2.10gen.cc 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386 BOOST_LIB_VERSION=1_40
      Mon Apr  9 12:56:02 [initandlisten] options: {}
      Mon Apr  9 12:56:02 [initandlisten] journal dir=/data/db/journal
      Mon Apr  9 12:56:02 [initandlisten] recover : no journal files present, no recovery needed
      Mon Apr  9 12:56:02 [websvr] admin web console waiting for connections on port 28017
      Mon Apr  9 12:56:02 [initandlisten] waiting for connections on port 27017
  5. Check out the JPA/NoSQL sample from SVN repository. The complete source code built in this TOTD can be downloaded here.
  6. Create Java EE 6 web app
    1. Create a Java EE 6 Maven web app as:
      mvn archetype:generate
      -DarchetypeGroupId=org.codehaus.mojo.archetypes
      -DarchetypeArtifactId=webapp-javaee6
      -DgroupId=model -DartifactId=javaee-nosql
      -DarchetypeVersion=1.5 -DinteractiveMode=false
    2. Copy the model files from the checked out workspace to the generated project as:
      cd javaee-nosql
      cp -r ~/code/workspaces/org.eclipse.persistence.example.jpa.nosql.mongo/src/model src/main/java
    3. Copy "persistence.xml"
      mkdir src/main/resources
      cp -r ~/code/workspaces/org.eclipse.persistence.example.jpa.nosql.mongo/src/META-INF ./src/main/resources
    4. Add the following dependencies:
      <dependency>
      <groupId>org.eclipse.persistence</groupId>
      <artifactId>org.eclipse.persistence.jpa</artifactId>
      <version>2.4.0-SNAPSHOT</version>
      <scope>provided</scope>
      </dependency>
      <dependency>
      <groupId>org.eclipse.persistence</groupId>
      <artifactId>org.eclipse.persistence.nosql</artifactId>
      <version>2.4.0-SNAPSHOT</version>
      </dependency>
      <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongo-java-driver</artifactId>
      <version>2.7.3</version>
      </dependency>

      The first one is for the EclipseLink latest APIs, the second one is for EclipseLink/NoSQL support, and the last one is the MongoDB Java driver.

      And the following repository:
      <repositories>
      <repository>
      <id>EclipseLink Repo</id>
      <url>http://www.eclipse.org/downloads/download.php?r=1&amp;nf=1&amp;file=/rt/eclipselink/maven.repo</url>
      <snapshots>
      <enabled>true</enabled>
      </snapshots>
      </repository> 
      </repositories>
    5. Copy the "Test.java" to the generated project:
      mkdir src/main/java/example
      cp -r ~/code/workspaces/org.eclipse.persistence.example.jpa.nosql.mongo/src/example/Test.java ./src/main/java/example/
      This file contains the source code to CRUD the JPA entity to MongoDB. This sample is explained in detail on EclipseLink wiki.
    6. Create a new Servlet in "example" directory as:
      package example;

      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;

      /**
      * @author Arun Gupta
      */
      @WebServlet(name = "TestServlet", urlPatterns = {"/TestServlet"})
      public class TestServlet extends HttpServlet {

      protected void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      response.setContentType("text/html;charset=UTF-8");
      PrintWriter out = response.getWriter();
      try {
      out.println("<html>");
      out.println("<head>");
      out.println("<title>Servlet TestServlet</title>");
      out.println("</head>");
      out.println("<body>");
      out.println("<h1>Servlet TestServlet at " + request.getContextPath() + "</h1>");
      try {
      Test.main(null);
      } catch (Exception ex) {
      ex.printStackTrace();
      }
      out.println("</body>");
      out.println("</html>");
      } finally {
      out.close();
      }
      }

      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      processRequest(request, response);
      }

      @Override
      protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      processRequest(request, response);
      }
      }
  7. Build the project and deploy it as:

    mvn clean package
    glassfish3/bin/asadmin deploy --force=true target/javaee-nosql-1.0-SNAPSHOT.war
  8. Accessing http://localhost:8080/javaee-nosql/TestServlet shows the following messages in the server.log:

    connecting(EISLogin(
    platform=> MongoPlatform
    user name=> ""
    MongoConnectionSpec()
    ))
    . . .

    Connected:
    User:
    Database: 2.7  Version: 2.7

    . . .

    Executing MappedInteraction()
    spec => null
    properties => {mongo.collection=CUSTOMER, mongo.operation=INSERT}
    input => [DatabaseRecord(
    CUSTOMER._id => 4F848E2BDA0670307E2A8FA4
    CUSTOMER.NAME => AMCE)]

    . . .


    Data access result: [{TOTALCOST=757.0, ORDERLINES=[{DESCRIPTION=table,
    LINENUMBER=1, COST=300.0}, {DESCRIPTION=balls, LINENUMBER=2, COST=5.0},
    {DESCRIPTION=rackets, LINENUMBER=3, COST=15.0}, {DESCRIPTION=net,
    LINENUMBER=4, COST=2.0}, {DESCRIPTION=shipping, LINENUMBER=5,
    COST=80.0}, {DESCRIPTION=handling, LINENUMBER=6, COST=55.0},
    {DESCRIPTION=tax, LINENUMBER=7, COST=300.0}], SHIPPINGADDRESS=
    [{POSTALCODE=L5J1H7, PROVINCE=ON, COUNTRY=Canada, CITY=Ottawa,
    STREET=17 Jane St.}], VERSION=2, _id=4F848E2BDA0670307E2A8FA8,
    DESCRIPTION=Pingpong table, CUSTOMER__id=4F848E2BDA0670307E2A8FA7,
    BILLINGADDRESS=[{POSTALCODE=L5J1H8, PROVINCE=ON, COUNTRY=Canada,
    CITY=Ottawa, STREET=7 Bank St.}]}]

    You'll not see any output in the browser, just the output in the console. But the code can be easily modified to do so.

Once again, the complete Maven project can be downloaded here.

Do you want to try accessing relational and non-relational (aka NoSQL) databases in the same PU ?

Sunday Jun 26, 2011

TOTD #166: Using NoSQL database in your Java EE 6 Applications on GlassFish - MongoDB for now!

The Java EE 6 platform includes Java Persistence API to work with RDBMS. The JPA specification defines a comprehensive API that includes, but not restricted to, how a database table can be mapped to a POJO and vice versa, provides mechanisms how a PersistenceContext can be injected in a @Stateless bean and then be used for performing different operations on the database table and write typesafe queries.

There are several well known advantages of RDBMS but the NoSQL movement has gained traction over past couple of years. The NoSQL databases are not intended to be a replacement for the mainstream RDBMS. As Philosophy of NoSQL explains, NoSQL database was designed for casual use where all the features typically provided by an RDBMS are not required. The name "NoSQL" is more of a category of databases that is more known for what it is not rather than what it is.

The basic principles of NoSQL database are:

  1. No need to have a pre-defined schema and that makes them a schema-less database. Addition of new properties to existing objects is easy and does not require ALTER TABLE. The unstructured data gives flexibility to change the format of data any time without downtime or reduced service levels. Also there are no joins happening on the server because there is no structure and thus no relation between them.
  2. Scalability and performance is more important than the entire set of functionality typically provided by an RDBMS. This set of databases provide eventual consistency and/or transactions restricted to single items but more focus on CRUD.
  3. Not be restricted to SQL to access the information stored in the backing database.
  4. Designed to scale-out (horizontal) instead of scale-up (vertical). This is important knowing that databases, and everything else as well, is moving into the cloud. RBDMS can scale-out using sharding but requires complex management and not for the faint of heart.
  5. Unlike RBDMS which require a separate caching tier, most of the NoSQL databases comes with integrated caching.
  6. Designed for less management and simpler data models lead to lower administration as well.
There are primarily three types of NoSQL databases:
  1. Key-Value stores (e.g. Cassandra and Riak)
  2. Document databases (MongoDB or CouchDB)
  3. Graph databases (Neo4J)
You may think NoSQL is panacea but as I mentioned above they are not meant to replace the mainstream databases and here is why:
  • RDBMS have been around for many years, very stable, and functionally rich. This is something CIOs and CTOs can bet their money on without much worry. There is a reason 98% of Fortune 100 companies run Oracle :-) NoSQL is cutting edge, brings excitement to developers, but enterprises are cautious about them.
  • Commercial databases like Oracle are well supported by the backing enterprises in terms of providing support resources on a global scale. There is a full ecosystem built around these commercial databases providing training, performance tuning, architecture guidance, and everything else. NoSQL is fairly new and typically backed by a single company not able to meet the scale of these big enterprises.
  • NoSQL databases are good for CRUDing operations but business intelligence is extremely important for enterprises to stay competitive. RDBMS provide extensive tooling to generate this data but that was not the original intention of NoSQL databases and is lacking in that area. Generating any meaningful information other than CRUDing require extensive programming.
  • Not suited for complex transactions such as banking systems or other highly transactional applications requiring 2-phase commit.
  • SQL cannot be used with NoSQL databases and writing simple queries can be involving.
Enough talking, lets take a look at some code.
This blog has published multiple blogs on how to access a RDBMS using JPA in a Java EE 6 application. This Tip Of The Day (TOTD) will show you can use MongoDB (a document-oriented database) with a typical 3-tier Java EE 6 application.
Lets get started! The complete source code of this project can be downloaded here.
  1. Download MongoDB for your platform from here (1.8.2 as of this writing) and start the server as:

    arun@ArunUbuntu:~/tools/mongodb-linux-x86_64-1.8.2/bin$./mongod
    ./mongod --help for help and startup options
    Sun Jun 26 20:41:11 [initandlisten] MongoDB starting : pid=11210
    port=27017 dbpath=/data/db/ 64-bit
    Sun Jun 26 20:41:11 [initandlisten] db version v1.8.2, pdfile version
    4.5
    Sun Jun 26 20:41:11 [initandlisten] git version:
    433bbaa14aaba6860da15bd4de8edf600f56501b
    Sun Jun 26 20:41:11 [initandlisten] build sys info: Linux
    bs-linux64.10gen.cc 2.6.21.7-2.ec2.v1.2.fc8xen #1 SMP Fri Nov 20
    17:48:28 EST 2009 x86_64 BOOST_LIB_VERSION=1_41
    Sun Jun 26 20:41:11 [initandlisten] waiting for connections on port 27017
    Sun Jun 26 20:41:11 [websvr] web admin interface listening on port 28017

    The default directory for the database is /data/db and needs to be created as:

    sudo mkdir -p /data/db/
    sudo chown `id -u` /data/db

    You can specify a different directory using "--dbpath" option. Refer to Quickstart for your specific platform.
  2. Using NetBeans, create a Java EE 6 project and make sure to enable CDI and add JavaServer Faces framework.
  3. Download MongoDB Java Driver (2.6.3 of this writing) and add it to the project library by selecting "Properties", "LIbraries", "Add Library...", creating a new library by specifying the location of the JAR file, and adding the library to the created project.
  4. Edit the generated "index.xhtml" such that it looks like:

    <h1>Add a new movie</h1>
    <h:form>
    Name: <h:inputText value="#{movie.name}" size="20"/><br/>
    Year: <h:inputText value="#{movie.year}" size="6"/><br/>
    Language: <h:inputText value="#{movie.language}" size="20"/><br/>
    <h:commandButton actionListener="#{movieSessionBean.createMovie}"
    action="show"
    title="Add"
    value="submit"/>
    </h:form>
    This page has a simple HTML form with three text boxes and a submit button. The text boxes take name, year, and language of a movie and the submit button invokes the "createMovie" method of "movieSessionBean" and then render "show.xhtml".
  5. Create "show.xhtml" ("New" -> "Other..." -> "Other" -> "XHTML File") such that it looks like:
     <head>
    <title><h1>List of movies</h1></title>
    </head>
    <body>
    <h:form>
    <h:dataTable value="#{movieSessionBean.movies}" var="m" >
    <h:column><f:facet name="header">Name</f:facet>#{m.name}</h:column>
    <h:column><f:facet name="header">Year</f:facet>#{m.year}</h:column>
    <h:column><f:facet name="header">Language</f:facet>#{m.language}</h:column>
    </h:dataTable>
    </h:form>

    This page shows the name, year, and language of all movies stored in the database so far. The list of movies is returned by "movieSessionBean.movies" property.
  6. Now create the "Movie" class such that it looks like:

    import com.mongodb.BasicDBObject;
    import com.mongodb.BasicDBObject;
    import com.mongodb.DBObject;
    import javax.enterprise.inject.Model;
    import javax.validation.constraints.Size;

    /**
    * @author arun
    */
    @Model
    public class Movie {
    @Size(min=1, max=20)
    private String name;

    @Size(min=1, max=20)
    private String language;

    private int year;

    // getters and setters for "name", "year", "language"

    public BasicDBObject toDBObject() {
    BasicDBObject doc = new BasicDBObject();

    doc.put("name", name);
    doc.put("year", year);
    doc.put("language", language);

    return doc;
    }

    public static Movie fromDBObject(DBObject doc) {
    Movie m = new Movie();

    m.name = (String)doc.get("name");
    m.year = (int)doc.get("year");
    m.language = (String)doc.get("language");

    return m;
    }

    @Override
    public String toString() {
    return name + ", " + year + ", " + language;
    }
    }

    Other than the usual boilerplate code, the key methods here are "toDBObject" and "fromDBObject". These methods provide a conversion from "Movie" -> "DBObject" and vice versa. The "DBObject" is a MongoDB class that comes as part of the mongo-2.6.3.jar file and which we added to our project earlier.  The complete javadoc for 2.6.3 can be seen here. Notice, this class also uses Bean Validation constraints and will be honored by the JSF layer.
  7. Finally, create "MovieSessionBean" stateless EJB with all the business logic such that it looks like:
    package org.glassfish.samples;

    import com.mongodb.BasicDBObject;
    import com.mongodb.DB;
    import com.mongodb.DBCollection;
    import com.mongodb.DBCursor;
    import com.mongodb.DBObject;
    import com.mongodb.Mongo;
    import java.net.UnknownHostException;
    import java.util.ArrayList;
    import java.util.List;
    import javax.annotation.PostConstruct;
    import javax.ejb.Stateless;
    import javax.inject.Inject;
    import javax.inject.Named;

    /**
    * @author arun
    */
    @Stateless
    @Named
    public class MovieSessionBean {

    @Inject Movie movie;

    DBCollection movieColl;

    @PostConstruct
    private void initDB() throws UnknownHostException {
    Mongo m = new Mongo();
    DB db = m.getDB("movieDB");
    movieColl = db.getCollection("movies");
    if (movieColl == null) {
    movieColl = db.createCollection("movies", null);
    }
    }

    public void createMovie() {
    BasicDBObject doc = movie.toDBObject();
    movieColl.insert(doc);
    }

    public List<Movie> getMovies() {
    List<Movie> movies = new ArrayList();
    DBCursor cur = movieColl.find();
    System.out.println("getMovies: Found " + cur.size() + " movie(s)");
    for (DBObject dbo : cur.toArray()) {
    movies.add(Movie.fromDBObject(dbo));
    }

    return movies;
    }
    }

    The database is initialized in @PostConstruct. Instead of a working with a database table, NoSQL databases work with a schema-less document. The "Movie" class is the document in our case and stored in the collection "movies". The collection allows us to perform query functions on all movies. The "getMovies" method invokes "find" method on the collection which is equivalent to the SQL query "select * from movies" and then returns a List<Movie>.

    Also notice that there is no "persistence.xml" in the project.
  8. Right-click and run the project to see the output as:



    Enter some values in the text box and click on enter to see the result as:



    If you reached here then you've successfully used MongoDB in your Java EE 6 application, congratulations!
Some food for thought and further play ...
In terms of this project, here are some tasks that can be tried:
  1. Encapsulate database management in a JPA persistence provider. Is it even worth it because the capabilities are going to be very different ?
  2. MongoDB uses "BSonObject" class for JSON representation, add @XmlRootElement on a POJO and how a compatible JSON representation can be generated. This will make the fromXXX and toXXX methods redundant.

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today