Saturday, May 21, 2011

Tapestry 5.1 on Jetty with MongoDB Part 1



This is the first time I am going to present a tutorial! The topic is, as the title already states, a step by step tutorial using the NoSQL database MongoDB with the according java-driver in Tapestry 5.

Following requirements have to be met before starting:

Requirements

  • Eclipse IDE (I chose 3.5.2 Galileo)
  • Eclipse Maven Plugin (m2eclipse sonatype)
  • Tapestry 5.1
  • mongo-java-driver 2.5.3
Set Up

I assume you already have set up Eclipse with Maven.

First step is to start Eclipse. Then go to File--> New--> Other --> Maven--> Maven Project
Click Next. Click Next again.
Type in "Filter" : org.apache.tapestry
Then choose the one with artifactId "quickstart" and version "5.1.0.5"
Click Next.
Type in your groupId, artifactId and click on Finish!

Dependencies

So now you got a basic Tapestry 5 Project correctly set up we need to configure additional dependencies we need to run mongoDB.

So rightclick on your project in the workspace --> chose maven --> add dependency
Type into the textfield "org.mongodb". Then choose the mongo-java-driver 2.5.3 or newer.



As we want to run our application on Jetty 7 we have to refactor the POM file a little bit.

Just replace everything in your "build" tag with this:
<build>
<finalName>tapestry_mongodb</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<optimize>true</optimize>
</configuration>
</plugin>

<!-- Run the application using "mvn jetty:run" -->
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty-version}</version>
<configuration>
<connectors>
<connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
<port>8080</port>
</connector>
</connectors>
</configuration>
</plugin>
</plugins>
</build>


Replace this at the bottom of your POM to set up the embedded jetty version.
<properties>
<tapestry-release-version>5.1.0.5</tapestry-release-version>
<jetty-version>7.1.6.v20100715</jetty-version>
</properties>

OK now that you have set up the basic part we can test run our application.
Rightclick your project in the workspace and choose "Run as" --> "Maven Build.."

In the upcoming popup type into the "Goal" field : "jetty:run".
That's it. You should see something like this when navigating to http://localhost:8080/




Remark: For simplicity I removed all standard archetype generated pages + .tml files to have a better overview later on.

Setting up MongoDB

  1. Get the MongoDB according to your OS at http://www.mongodb.org/downloads
  2. Install it
  3. Run it
Setting up Tapestry to work with MongoDB

1. Creating a Tapestry Service to interact with MongoDB:

As we want to have access in our Tapestry Page through injecting a service we have to create a new Java Class in the services package. Lets call it MongoDBHandler. This class has to implements the interface IMongoDBhandler which we also create in the same package.
This looks like this:




There is only one step left, in order to access this service: We have to configure it in the global AppModule Class that Tapestry IOC knows where to find the service. Go into "AppModule" class and add following into the "bind" Method
public static void bind(ServiceBinder binder) {
binder.bind(IMongoDBHandler.class, MongoDBHandler.class);
}

Lets have a look at the MongoDBhandler Class and add some basic stuff to connect with our mongoDB. This will look something like this:



package de.boehme.app.tapestry_mongodb.services;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.apache.tapestry5.annotations.Log;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

public class MongoDBHandler implements IMongoDBHandler{

private DB db;

public MongoDBHandler() throws UnknownHostException, MongoException{
//set up db connection
connectToDatabase();
}

@Log
private void connectToDatabase() throws UnknownHostException, MongoException{
Mongo m = new Mongo();
this.db = m.getDB( "tapestry_mongodb" );
}
}


What does these code lines do? Well when we inject this service into a page and it will be used for the first time across the whole application it gets instantiated, thus the constructor gets called which leads to a connection to the MongoDB. Additionally we "activate" the database "tapestry_mongodb". If this database does not exist it gets automatically created.

Now lets prepare this service with a real example:
For instance we would like to create a new "User" and save it to the database. Therefore we need to add new method called "createUser" and for the output a "getAllusers" method.

add this to MongoDBHandler.java

public void createUser(String userName) {
//create a new BSON Object
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("userName", userName);
//now get the collection
DBCollection coll = db.getCollection("userCollection");
coll.insert(dbObject);
}

public List<String> getAllUsers(){
List<String> result = new ArrayList<String>();
DBCollection coll = db.getCollection("userCollection");
DBCursor cur = coll.find();
while(cur.hasNext()) {
BasicDBObject dbObject = (BasicDBObject) cur.next();
result.add(dbObject.getString("userName"));
}
return result;
}

add this to IMongoDBhandler.java
   public void createUser(String userName);
public List<string> getAllUsers();

Now we can finally create our Tapestry Page.

2. Create a new Tapestry Page

Go to the "pages" package and create a new class "UserAdmin". The class will look like this:

UserAdmin.java
package de.boehme.app.tapestry_mongodb.pages;

import java.util.List;

import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.ioc.annotations.Inject;

import de.boehme.app.tapestry_mongodb.services.IMongoDBHandler;

public class UserAdmin {

@Inject
private IMongoDBHandler mongoDB;

@Property
private String userName;

@Property
private List<String> users;

void onActivate(){
users = mongoDB.getAllUsers();
}

Object onSuccess(){
mongoDB.createUser(userName);
users = mongoDB.getAllUsers();
return this;
}

}

Now got to src/main/webapp and create the according .tml file:

UserAdmin.tml

<html t:title="tapestry_mongodb Useradmin"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
xmlns:p="tapestry:parameter">

<h1>Create User</h1>
<form t:type="form">
<t:errors />
<input t:type="textfield" t:id="username"/>
<input type="submit" value="Create User" />
</form>

<h1>User List</h1>
<t:loop source="users" value="userName">
<h2>User: ${userName}</h2>
</t:loop>
</html>



Doing the first test and seeing the result

Now start up jetty with "jetty:run" and navigate in your browser to http://localhost:8080/useradmin

You will see following:



If you enter now a name e.g. Admin and click on "Create User" a new document will be created and saved to MongoDB. Additionally you see a full list of all "Users" underneath.

That's mainly everything for Part 1.
The next part will be about using DTO's in order to be more object-oriented. And we will build our own ORM in order to get the most out of mongoDB!

Note: Full source code as maven project avaiable here: tapestry_mongodb.zip

Go on to the 2nd part Tapestry 5.1 on Jetty with MongoDB Part 2

3 comments:

  1. It wont work for me, i'm getting this exception while loading page:

    [DEBUG] AppModule.IMongoDBHandler Invoking constructor public project.services.MongoDBHandler() throws java.net.UnknownHostException,com.mongodb.MongoException (for service 'IMongoDBHandler')

    Please help

    ReplyDelete
    Replies
    1. Mongod is started, and i get connection accepted from 127.0.0.1:51392 but no data is saved

      Delete
  2. Works perfect, thx a lot.

    For mongo-java-driver 2.11.0 Mongo constructor is deprecated, update to ...

    this.client = new MongoClient("localhost", 27017);
    this.db = client.getDB("tapestry_mongodb");

    MongoException no longer needed.

    As far as i know, in tapestry v. 5.4.x mongodb support will be integrated.

    [at]see: https://github.com/apache/tapestry-5

    ReplyDelete