REST Java Architecture

9:14 PM Dani 1 Comments

In this article I am going to walk you through the implementation of a complete client/server RESTful Java web application that fully conforms to the Representational State Transfer (REST) constraints. Restful web services are well suited for the creation of APIs to be exposed to the internet for later consumptions. These APIs provide access to the resources and functionality exposed by the web application and can be accessed by the Unified Resource Identifiers (URIs). RESTful architecture is designed to use a stateless communication protocol (HTTP) which makes the application more scalable both horizontally and vertically, load balanced and easier to deal with upon fail-overs.

What makes RESTful applications to be simple, lightweight and fast?

- Resource identification through URI: Global addressing space for resource and service discovery.
-Uniform interface: Resources are manipulated using a set of operations (CRUD - Create/Read/Update/Delete) determined by the standard HTTP methods (GET, POST, PUT, DELETE) to which the resource is responding.
- Self-descriptive messages: Content is decoupled in a way that it can be accessed in a variety of formats such as HTML, XML, JSON, plain text, etc.
- Stateful interaction through links.
Bearing the above summary regarding REST in mind, let’s get into more detail explaining all the components that will be part of the architecture:

- RESTEasy: RESTful framework fully complaint with the JAX-RS specification and therefore it ensures that our web services will follow the REST architectural pattern. This will allow us to create the different services that will make our application reachable from the internet.
- Google Guice: Dependency injection framework that helps our app to be more reusable, testable and maintainable. Moreover, with this in place, we ensure that all the amount of workload needed for complicated tasks such as the inversion of control and dependency injection are properly addressed and performed.
- MyBatis: Persistance framework in charge of managing and handling the Object relational model (ORM).
- SLF4J/Logback: Logback will be the framework chosen for all the logging tasks. Since it implements SLF4J, which is the logging facade, we can readily switch to other logging frameworks such as log4j making our app’s logging pretty flexible.

Figure 1: Diagram showing the software stack used for the architecture.

Now that we have a more clear view on the general picture, let’s get the ball rolling setting up the project. In order to have an organized build procedure and all the dependencies that our project will rely on we will be using Maven as the build automation tool. Given that, let’s start off by configuring all the Maven projects (different modules) that will compose our architecture.


2.- Setting up a maven project with all the modules needed.


As a general rule of thumb, prior to creating any project structure we have to spend some time and think what will be the needs of the project. Doing it so will probably compensate the efforts undertaken and result into a more clean, decoupled and modularized project.

Considering that we already did a deep analysis of the components that our app will have and looking at the diagram aforementioned, we can go ahead and conclude that the following modules will need to be created:

api-module: This project will serve as the front face of our app. We will be exposing different end points that will provide the point of entrance for all the API calls. In order to create this project we will need to make use of a slightly different archetype since this will be the actual web app of our application.
model-module: Project containing all the business model objects that will be used in our application. 
orm-module: Module having all the functionally concerning the DB such as the Data Access Objects (DAOs), mappers, interceptors (transactions, paging, etc).
utils-module: Functionality that can potentially be shared across all the projects. For instance, exception handling, context holders, etc.
integration-module: This module will be in charge of all the unit testing. Basically this module will insure that the libraries above are generated (after running mvn package/install) only in the case where all the test cases successfully passed. Otherwise, the build will through an error and will serve as a hint that something is wrong in our code.

First and foremost, we need to create the parent maven project which will let us compile, build and test all the modules at once. Additionally, it will be the centralized place to keep track of the project's versions/snapshots as well as the shared dependencies across the projects. In order to do this, we run the following command on the directory where all our projects will reside in (e,g: /Users/xxxx/Development/Architectures/RESTFul/trunk):

➜  trunk > mvn archetype:generate -DgroupId=com.creacodetive -DartifactId=creacodetive-parent

In order to be able to aggregate multiple projects that will have this project as a parent and thus inherit all the dependencies set within it, the POM file (automatically created upon the command completion) will need to be updated. Open up the POM file in any editor and update the packaging tag (automatically set as jar) to be:

<packaging>pom</packaging>

Now access the parent directory and start creating the different modules that will be part of our  application.

➜  trunk > cd creacodetive-parent -DinteractiveMode=false

Notice that the api module is generated using a different archetype artifact id - 442 (maven-archetype-webapp). The reason for this is basically because the api will act as the actual web application of our project.

➜  creacodetive-parent > mvn archetype:generate -DgroupId=com.creacodetive -DartifactId=creacodetive-api -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

The rest of the modules will use exactly the same default archetype artifact id - 439 (maven-archetype-quickstart).

➜  creacodetive-parent > mvn archetype:generate -DgroupId=com.creacodetive -DartifactId=creacodetive-model -DinteractiveMode=false
➜  creacodetive-parent > mvn archetype:generate -DgroupId=com.creacodetive -DartifactId=creacodetive-orm -DinteractiveMode=false
➜  creacodetive-parent > mvn archetype:generate -DgroupId=com.creacodetive -DartifactId=creacodetive-utils -DinteractiveMode=false
➜  creacodetive-parent > mvn archetype:generate -DgroupId=com.creacodetive -DartifactId=creacodetive-integration -DinteractiveMode=false

At this point all the projects should be created. However, we still need to do some adjustments inside the pom files just to have the projects better structured and have more meaningful information when running the "mvn" command upon the POM files.

In the parent POM, update the module tags (automatically generated when we created the projects under the parent folder) with the following:

<module>../creacodetive-api</module>
<module>../creacodetive-model</module>
<module>../creacodetive-orm</module>
<module>../creacodetive-utils</module>
<module>../creacodetive-integration</module>

The main purpose of doing this is to have all the projects living under the same root rather than having the different modules needed under the parent folder. Personally, I consider this set up more convenient and easy to understand but of course it is just a personal preference and the previous lines can be left off as is should you rather have the initial setup.

In the remaining POMs we are going to update/insert some other tags that will provide meaningful information upon the projects compilation about each project accordingly:

creacodetive-api:
<name>Creacodetive API Server</name>
<description>Creacodetive:: API Server Module</description>
<url>http://www.creacodetive.com</url>

creacodetive-model:
<name>Creacodetive Model</name>
<description>Creacodetive:: Model module</description>
<url>http://www.creacodetive.com</url>

creacodetive-orm:
<name>Creacodetive Orm</name>
<description>Creacodetive:: ORM module</description>
<url>http://www.creacodetive.com</url>

creacodetive-utils:
<name>Creacodetive Utils</name>
<description>Creacodetive:: Utils module</description>
<url>http://www.creacodetive.com</url>

creacodetive-integration:
<name>Creacodetive Integration</name>
<description>Creacodetive:: Integration module</description>
<url>http://www.creacodetive.com</url>

After applying the above updates and running the following command you will be able to see in the log how the projects are built and the test cases are executed. Thus, having provided the above details to the POM projects the logs will be more clear showing meaningful information like the one screenshot below:

➜  creacodetive-parent   mvn clean install

Figure 2: Maven project install build order

Figure 3: Maven project install summary

Download the skeleton of the app here: RESTFul-App-Skeleton.zip

3.- Importing the project into Eclipse IDE


Having reached this point, we should be able to import the modules created previously into Eclipse (Version: Kepler Service Release 1) which will be the IDE used in this tutorial. First we need to create a workspace for the project. After the latter, we need to import the already created maven projects doing the following:

File -> Import -> Maven -> Existing Maven Projects

Figure 4: Importing Maven projects into Eclipse IDE

Upon project successful import, the explorer should look like the following screenshot:

Figure 5: Project Explorer view

Congratulations, if you have made it this far you should be able to actually begin with the development of the application!

4.- Adding RESTEasy to the application


Firstly we need to add resteasy's artifact to our creacodetive-api project's pom.xml configuration so Maven will automatically download all its dependencies for us.


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
        <groupId>com.creacodetive</groupId>
        <artifactId>creacodetive-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../creacodetive-parent/pom.xml</relativePath>
</parent>

<artifactId>creacodetive-api</artifactId>
<packaging>war</packaging>

<name>Creacodetive API Server</name>
<description>Creacodetive:: API Server Module</description>
<url>http://www.creacodetive.com</url>

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>3.8.1</junit.version>
<resteasy-jaxrs.version>2.2.1.GA</resteasy-jaxrs.version>
<resteasy-jackson-provider.version>2.2.1.GA</resteasy-jackson-provider.version>
</properties>

<repositories>
        <repository>
                <id>JBoss repository</id>
                <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
        </repository>
</repositories>

<dependencies>

        <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
        </dependency>
        <dependency>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-jaxrs</artifactId>
                <version>${resteasy-jaxrs.version}</version>
        </dependency>

        <!-- Jackson provider will convert the objects returned to JSON automatically when RESTEasy
        is returning a JSON output (@Produces("application/json")). -->
        <dependency>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-jackson-provider</artifactId>
                <version>${resteasy-jackson-provider.version}</version>
        </dependency>

</dependencies>

<build>
        <finalName>creacodetive-api</finalName>
</build>

</project>


As you can see below, the libraries highlighted have been automatically resolved and downloaded by Maven right after we included the dependency in our POM:

Figure 6: RESTEasy maven dependencies

We are using 2.2.1.GA version as it is an stable version; however other available versions could have been used as well and would have been worked just as fine as 2.3.7. More info about version available here:
Rest Easy JAX-RS Implementation Maven repository

Secondly, we need to set up the application's web.xml with the RESTEasy configuration.

<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>Creacodetive API :: Restful Web Application</display-name>

<!-- Auto scan REST service -->
<context-param>
        <param-name>resteasy.scan.resources</param-name>
        <param-value>true</param-value>
</context-param>

<!-- this need same with resteasy servlet url-pattern -->
<context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>/api</param-value>
</context-param>

<listener>
        <listener-class>
                org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
        </listener-class>
</listener>

<servlet>
        <servlet-name>resteasy-servlet</servlet-name>
        <servlet-class>
                org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
        </servlet-class>
</servlet>

<servlet-mapping>
        <servlet-name>resteasy-servlet</servlet-name>
        <url-pattern>/api/*</url-pattern>
</servlet-mapping>

<welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

</web-app>

The third step should be the inclusion of the 'src/main/java' folder into the build path. Furthermore, for organization purposes we are going to create a package (com/creacodetive/api/services/) that will contain all the services exposed through REST:

Figure 7: Creating package for services


In order to be able to test that RESTEasy is actually functional let's create a new class PublicService.java under the package created before with the following content:

package com.creacodetive.api.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

@Path("/public")
public class PublicService
{

        @GET
        @Path("/{param}")
        public Response printMessage(@PathParam("param") String msg)
        {
                String result = String.format("Restful printing message: %s", msg);
                return Response.status(200).entity(result).build();
        }
}

And the project explorer should look like the following after having created the public service class:

Figure 8: Creating PublicService java class


Lastly, we need to test the service starting up the application in an application server (e,g: Apache Tomcat) and calling the service created previously as follows:

http://localhost:8080/creacodetive-api/api/public/Welcome%20to%20the%20REST%20World!

See below a brief description of what the URL is composed of:

- Server URL: http://localhost:8080
- Context of the Application to run: /creacodetive-api
- RESTEasy servlet mapping prefix: /api
- Resource Path: /public/{path_param}
    - {path_param} Content passed to the resource: Welcome%20to%20the%20REST%20World!

At this point we can now say that we've got RESTEasy integrated in our app.

Download the api module containing the RESTEasy framework here: creacodetive-api with RESTEasy or the whole project including RESTEasy here: RESTful Java App

5.- Creating the persistence layer - Mybatis


Mybatis will be the framework that will help us handle all the operations performed against the relational DB which in this tutorial will be MySQL. The reason why I have decided to use Mybatis is because of its seamless integration with Google Guice - dependency injection framework that we will be using and that will be covered in the next section.

For the sake of concreteness, I am not going to cover how to set up a MySQL instance locally on your computer. In this tutorial, we are going to assume that the reader has already installed a functional MySQL server.

First off we need to add the proper dependencies in order to be able to use Mybatis. Hance, let's get into it updating the pom.xml file inside the ORM module (creacodetive-orm) with the following:

<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>

<parent>
        <groupId>com.creacodetive</groupId>
        <artifactId>creacodetive-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../creacodetive-parent/pom.xml</relativePath>
</parent>

<artifactId>creacodetive-orm</artifactId>
<packaging>jar</packaging>

<name>Creacodetive Orm</name>
<description>Creacodetive:: ORM module</description>
<url>http://www.creacodetive.com</url>

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>4.10</junit.version>
        <org.mybatis.version>3.1.1</org.mybatis.version>
        <mysql.version>5.1.21</mysql.version>
</properties>

<build>
        <plugins>
                <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>2.3.2</version>
                        <configuration>
                                <source>1.6</source>
                                <target>1.6</target>
                                <encoding>${project.build.sourceEncoding}</encoding>
                        </configuration>
                </plugin>
        </plugins>
</build>

<dependencies>

        <!-- Creacodetive dependencies -->
        <dependency>
                <groupId>com.creacodetive</groupId>
                <artifactId>creacodetive-model</artifactId>
                <version>${project.version}</version>
        </dependency>
        <dependency>
                <groupId>com.creacodetive</groupId>
                <artifactId>creacodetive-utils</artifactId>
                <version>${project.version}</version>
        </dependency>

        <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>${org.mybatis.version}</version>
                </dependency>
        <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
                <scope>runtime</scope>
        </dependency>
</dependencies>

</project>
Maven configuration file (creacodetive-orm project): Adding Mybatis and MySQL driver dependencies

Including these dependencies will allow us to be able to use the Object-Relational Mapping framework within our app as well as perform all kind of operations through the mysql driver. Nevertheless, some configuration work is still needed in order to be able to use Mybatis. Mybatis requires a configuration file (mybatis-config.xml) to be set thus it can figure out all the details about the DB and how to properly make the connection to the database.
Therefore, we need to create a mybatis-config.xml file under the build path src/main/resources. That's the location where Mybatis will expect to find the configuration file.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
        <properties resource="jdbc.properties" />
        <typeAliases>
                <typeAlias type="com.creacodetive.model.User" alias="User"></typeAlias>
        </typeAliases>
        <environments default="development">
                <environment id="development">
                        <transactionManager type="JDBC" />
                        <dataSource type="POOLED">
                        <property name="driver" value="${jdbc.driverClassName}" />
                        <property name="url" value="${jdbc.url}" />
                        <property name="username" value="${jdbc.username}" />
                        <property name="password" value="${jdbc.password}" />
                        </dataSource>
                </environment>
        </environments>
<mappers>
        <mapper resource="com/creacodetive/mappers/UserMapper.xml" />
</mappers>
</configuration>
Configuration File (src/main/resources): mybatis-conf.xml

As you might have noticed, all the DB configuration values are coming from the properties file (jdbc.properties). This is really handy as it gives a lot of flexibility in case we wanted to have different configurations for different environments (dev, qa, pre-prod, etc). Note that the properties file must be located in the same directory as the mybatis-conf.xml file (as specified on the tag ), otherwise it won't be able to find it.

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/creacodetive
jdbc.username=root
jdbc.password=mysqladmin
Properties File (src/main/resources): jdbc.properties

Having reached this point, your orm project should look like the screenshot below:

Figure 9: General creacodetive-orm project view

In order to use Mybatis in any service class (e,g: UserService.class) we must bind the configuration file (mybatis-config.xml) with Mybatis libraries beforehand. In this part of the tutorial, we have created a utils class containing the SQLSessionFactory using the Singleton pattern which restricts the instantiation of this class to just one object. This approach is rather convinient as calling the getSqlSessionFactory() method will always return the same instance of the SqlSessionFactory object. It should be noted though that this is a temporary solution since the creation of the instances will actually be handled by the DI framework Google Guice in the next section. However, this should be enough for now to give us an introductory understanding on how much power the DI framework can bring to the project and how it will take care of complex operations that would be hard to manage otherwise.

package com.creacodetive.utils;

import java.io.IOException;
import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisUtil {

  private static SqlSessionFactory factory;

  private MyBatisUtil() {
  }

  static
  {
   Reader reader = null;
   try {
    reader = Resources.getResourceAsReader("mybatis-config.xml");
   } catch (IOException e) {
    throw new RuntimeException(e.getMessage());
   }
   factory = new SqlSessionFactoryBuilder().build(reader);
  }

  public static SqlSessionFactory getSqlSessionFactory() 
  {
   return factory;
  }
 
}
Mybatis Utils File (src/main/java/com/creacodetive/utils/MyBatisUtil.java) | creacodetive-orm

Congrats again! Your project is now able to connect to the DB and perform SQL operations. Mmhm wait a sec, we are not done yet. In order for that to be utterly true, we first need to actually test it! Since we already have RESTEasy working just fine, why don't we kill two birds with the same stone and test the whole work flow? With this I mean, calling a REST service end point (e,g: UserService) that will handle all the CRUD operations for the users. This way we will ensure that our orm-project is fully functional and will give us some hands-on experience on both REST and Mybatis.

First let's create the User.java business class inside the model project (creacodetive-model) which will be used by both Mybatis and RESTEasy. The former will utilize the User object-oriented domain model to map the entries in the relational database. Moreover, the User class will be used to persist data to the database without having to deal with JDBC code, manual setting of settings and retrieval of results. The latter (RESTEasy) will handle the tedious tasks of marshalling and un-marshalling of the different message bodies thus we can focus on other parts of the application.

Figure 10: General creacodetive-model project view

With the User model object in place we can go ahead and create the corresponding mapping file and its mapper interface. Now I guess you must be wondering, how does Mybatis know where to find the object model class and the mapper?. Let's back up a bit to where we set up the Mybatis (mybatis-conf.xml) configuration. As you can notice, there are two different tags <typeAlias> (refering to the model location) and  <mapper> (refering to the mapper location) which are the ones letting Mybatis know where to find both the business object and the corresponding mapper.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.creacodetive.mappers.UserMapper">

 <select id="getUserById" parametertype="int" resulttype="com.creacodetive.model.User">
  SELECT
  user_id as userId,
  email_id as emailId ,
  password,
  first_name as firstName,
  last_name as lastName
  FROM USER
  WHERE USER_ID = #{userId}
 </select>
 <!-- Instead of referencing Fully Qualified Class Names we can register 
  Aliases in mybatis-config.xml and use Alias names. -->
 <resultmap id="UserResult" type="User">
  <id column="user_id" property="userId">
  <result column="email_id" property="emailId">
  <result column="password" property="password">
  <result column="first_name" property="firstName">
  <result column="last_name" property="lastName">
 </result></result></result></result></id></resultmap>

 <select id="getAllUsers" resultmap="UserResult">
  SELECT * FROM USER
 </select>

 <insert id="insertUser" keyproperty="userId" parametertype="User" usegeneratedkeys="true">
  INSERT INTO USER(email_id, password, first_name, last_name)
  VALUES(#{emailId}, #{password}, #{firstName}, #{lastName})
 </insert>

 <update id="updateUser" parametertype="User">
  UPDATE USER
  SET
  PASSWORD= #{password},
  FIRST_NAME = #{firstName},
  LAST_NAME = #{lastName}
  WHERE USER_ID = #{userId}
 </update>

 <delete id="deleteUser" parametertype="int">
  DELETE FROM USER WHERE USER_ID = #{userId}
 </delete>

</mapper>
Mapper File (src/main/resources/com/creacodetive/mappers/UserMapper.xml) | creacodetive-orm

Following Mybatis specification, every mapper file should have its own associated mapper interface class as shown as follows. Note that for each operation in the mapper there is a function in the interface that captures the functionality.

package com.creacodetive.mappers;

import java.util.List;

import com.creacodetive.model.User;

public interface UserMapper {

  public void insertUser(User user);

  public User getUserById(Integer userId);

  public List getAllUsers();

  public void updateUser(User user);

  public void deleteUser(Integer userId);
 
}
Mapper Interface File (src/main/java/com/creacodetive/mappers/UserMapper.java)  | creacodetive-orm

Great, at this stage we have all what we need to give the user service a shot and test whether all the pieces work together. Nevertheless before doing it so, we have to slightly adjust the pom file inside the creacodetive-api project to include the orm and module dependencies. Otherwise, we will not be able to use the model and orm projects.
<!-- Creacodetive dependendies -->
  <dependency>
   <groupId>com.creacodetive</groupId>
   <artifactId>creacodetive-model</artifactId>
   <version>${project.version}</version>
  </dependency>
  <dependency>
   <groupId>com.creacodetive</groupId>
   <artifactId>creacodetive-orm</artifactId>
   <version>${project.version}</version>
  </dependency>

Figure 11:General creacodetive-api project view

Furthermore, the User service containing all the JAX-RS CRUD operations still needs to be implemented:

package com.creacodetive.mappers;
package com.creacodetive.api.services;

import java.util.List;

import javax.ws.rs.Consumes;
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.apache.ibatis.session.SqlSession;

import com.creacodetive.mappers.UserMapper;
import com.creacodetive.model.User;
import com.creacodetive.utils.MyBatisUtil;

/**
 * 
 * CRUD Operations:
 *  - Create - POST
 *  - Read - GET
 *  - Update - POST
 *  - Delete - DELETE
 * @author dikhanr
 *
 */
@Path("/users")
public class UserService {

 /**
  * @param user
  */
 @PUT
 @Consumes("application/json")
 public void insertUser(User user) 
 {
  SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()
    .openSession();
  try {
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   userMapper.insertUser(user);
   sqlSession.commit();
  } finally {
   sqlSession.close();
  }
 }
 
 /**
  * @param userId
  * @return
  */
 @GET
 @Produces("application/json")
 @Path("/{userId}")
 public User getUserById(@PathParam("userId") Integer userId) 
 {
  SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()
    .openSession();
  try {
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   return userMapper.getUserById(userId);
  } finally {
   sqlSession.close();
  }
 }

 /**
  * @return
  */
 @GET
 @Produces
 public List getAllUsers() 
 {
  SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()
    .openSession();
  try {
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   return userMapper.getAllUsers();
  } finally {
   sqlSession.close();
  }
 }

 /**
  * @param userId
  * @param user
  */
 @POST
 @Consumes("application/json")
 @Path("/{userId}") //@NotNull @NotEmpty
 public void updateUser(@PathParam("userId") Integer userId, User user) 
 {
  SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()
    .openSession();
  try {
   UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
   userMapper.setUserId(userId);
userMapper.updateUser(user); sqlSession.commit(); } finally { sqlSession.close(); } } /** * @param userId */ @DELETE @Consumes("application/json") @Path("/{userId}") public void deleteUser(@PathParam("userId") Integer userId) { SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory() .openSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.deleteUser(userId); sqlSession.commit(); } finally { sqlSession.close(); } } }
User Service class (src/main/java/com/creacodetive/api/services/UserService.java) | creacodetive-api

The moment of the truth has arrived and therefore let's do some smoke testing so as to check that the REST services implemented above are working as expected. I usually work with Advance REST Client, an extension for chrome, which comes in really handy when testing http requests and analysing the responses.
Important Note: Prior to starting calling the end points make sure that the project creacodetive-api is up and running under Tomcat and that the MySQL service is running as well.

Test 1: Creating a new user
           
       HTTP Method: PUT
       URL: http://localhost:8080/creacodetive-api/api/users
       Body Payload:
       {
              "emailId":"dikhan@creacotive.com",
              "password":"password01!",
              "firstName":"Daniel Isaac",
              "lastName":"Khan Ramiro"
       }
       Content-Type: application/json
       Response Status 204 No Content

Test 2: Retrieving a user by ID

       HTTP Method: GET
       URL: http://localhost:8080/creacodetive-api/api/users/1
       Response Status 200 OK

Test 3: Retrieving all the users

       HTTP Method: GET
       URL: http://localhost:8080/creacodetive-api/api/users
       Response Status 200 OK

Test 4: Updating existing user by ID

       HTTP Method: POST
       URL: http://localhost:8080/creacodetive-api/api/users/1
       Body Payload:
       {
               "emailId":"dikhan@creacotive.com",
               "password":"passwordsecret",
               "firstName":"Daniel",
               "lastName":"Khan"
       }
       Content-Type: application/json
       Response Status 204 No Content

Test 5: Deleting user by ID

       HTTP Method: DELETE
       URL: http://localhost:8080/creacodetive-api/api/users/1
       Response Status 204 No Content

Download the whole project including the new orm and module projects here.

6.- Adding the DI framework - Google Guice

Well done reader for making it this far. In this last section the main goal will be to integrate the dependency injection (DI) framework with what we currently have integrated being that both RESTEasy and MyBatis libraries. The idea is to hand over to Google Guice all the responsibility for the creation of all the object instances and the injection of those in the classes that depend on them.

Primarily, same as in previous sections, we have to prepare the POM files so the projects have all what they need for the integration with Google Guice.

creacodetive-api: As you may notice, the only additions to the file have been the Google Guice libraries, the rest remain exactly the same as before. These artifacts - 'guice-servlet' and 'resteasy-guice' will allow us to hand off the initialization of the dependencies to Google Guice through the GuiceResteasyBootstrapServletContextListener which will be covered soon.

<name>Creacodetive API Server</name>
 <description>Creacodetive:: API Server Module</description>
 <url>http://www.creacodetive.com</url>

 <properties>
  <guice.version>3.0</guice.version>
  <guice-servlet.version>3.0-rc3</guice-servlet.version>
  <resteasy-guice.version>3.0.6.Final</resteasy-guice.version>
 </properties>

 <dependencies>

  <!-- Google Guice -->
  
  <dependency>
   <groupId>com.google.inject.extensions</groupId>
   <artifactId>guice-servlet</artifactId>
   <version>${guice-servlet.version}</version>
  </dependency>
  
  <dependency>
   <groupId>org.jboss.resteasy</groupId>
   <artifactId>resteasy-guice</artifactId>
   <version>${resteasy-guice.version}</version>
  </dependency>

 </dependencies>


creacodetive-orm: In this POM the only update needed will be the addition of the dependency for the integration between mybatis and Google Guice - mybatis-guice. Having this in place will help us reducing the boilerplate and redundant code that would be needed if we were not using it.

<name>Creacodetive Orm</name>
 <description>Creacodetive:: ORM module</description>
 <url>http://www.creacodetive.com</url>

 <properties>
  <mybatis-guice>3.2</mybatis-guice>
 </properties>

 <dependencies>

  <dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-guice</artifactId>
   <version>${mybatis-guice}</version>
 </dependency>

 </dependencies>

Perfect, now it's time to actually configure the integration so let's get into it. As you might be suspecting already, the place to set all this up is the web.xml inside the api project.

There are three important changes here that need to be explained in more detail.

- First and foremost the RestEasyBootstrap listener has been substituted by GuiceResteasyBootstrapServletContextListener. This is relevant as this servlet will be in charge of starting up and injecting all the services provided by RestEasy through the APIGuiceModule as well as all the Mybatis mappers through the OrmGuiceModule.

<listener>
  <listener-class>org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener</listener-class>
 </listener>

- Both modules are called by the MainGuiceModule.java class which is a context param variable set in the web.xml too.

<!-- Context param needed to make resteasy.guice bootstrap aware of the guice modules -->
    <context-param>
        <param-name>resteasy.guice.modules</param-name>
        <param-value>com.creacodetive.api.inject.MainGuiceModule</param-value>
    </context-param>

Looking more carefully inside these modules:

- The ApiGuiceModule in this example is initializing just the manager and services. However, it is worth pointing out that this module could be extended much further to include for instance the initialization of various contexts such as UserContextHolder (RequestsScoped storing user's information in the session), PagingContextHolder (RequestScope managing paging per request) or ApiTransaction (RequestScoped for logging all the transactions). Furthermore, providers in charge of giving instances for the following interceptors SecurityInterceptor, ReponseInterceptor, RequestInterceptors can also be included in this module.

- The OrmGuiceModule also initializes some classes and binds them into Google Guice. Although in this specific module we are just binding the PooledDataSourceProvider, JdbcTransactionFactory as well as the DAOs and the mappers, we could have also added some interceptors such as a DataBaseInterceptor (looking for info in the context when performing the query) or PagingInterceptor among other things.

Additionally, one common aspect between the two modules (api and orm) is the binding of the properties inside the properties files into the Google Guice injector. This is really handy specially when you need these properties inside certain classes and can easily be injected on the fly by just writing few lines of code using the following annotation @Inject @Named. In our example, we make use of this powerful functionality when starting up Mybatis. The orm.properties resource file contains all the properties that will be used by the PooledDataSourceProvider as shown below:

@Inject
    public PooledDataSourceProvider(@Named("JDBC.driver") final String driver,
            @Named("JDBC.url") final String url,
            @Named("JDBC.username") final String username,
            @Named("JDBC.password") final String password) {
        this.dataSource = new PooledDataSource(this.getClass().getClassLoader(), driver, url, username, password);
    }

Hence, if you are trying to run this in your local environment you might want to adjust the properties to match your MySQL instance configuration.

- Lastly we have to define a new filter to let Google Guice be aware of all the requests.
<filter>
  <filter-name>guiceFilter</filter-name>
  <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>

Heads up! It is really important that prior to actually starting up the web application you check whether the auto scan (resteasy.scan.resources) tag is still part of the web.xml. If so, it will have to be removed as 
org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener is already taking care of the initialization of the REST services and binding them to Google Guice. Otherwise we will have redundant initializations causing potential exceptions like the following on startup:

SEVERE: Exception sending context initialized event to listener instance of class org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener
java.lang.AbstractMethodError
 at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:120)
 at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:106)
 at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:83)
 at org.jboss.resteasy.plugins.guice.ModuleProcessor.processInjector(ModuleProcessor.java:57)
 at org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener.contextInitialized(GuiceResteasyBootstrapServletContextListener.java:58)
 at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4939)
 at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434)
 at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
 at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)

The web.xml can be downloaded here.

Download the whole project including RESTEasy, MyBatis and Google Guice here.

For the sake of concreteness I am not going to go into too much detail about the general structure of the whole project. With that I mean describing each of the layers created for modularization and maintainability purposes. The main reason of this structure though is due to the flexibility it brings when for instance the implementation of certain interface has to change or we might want to have different implementations for different environments. Nonetheless, just so it is clear enough the following diagram describes the request and response cycle passing through all the layers:

Figure 12: HTTP Stack

And for your reference, please find below the respective eclipse screenshots of the api and orm projects:

                                    Figure 13: Api Eclipse project                                    Figure 14: Orm Eclipse Project                                                                                           


Just to wrap up this tutorial, please take your time to read through the code and try to understand all the components involved in it. Also, in order to ensure that the end points are still functional after the Google Guice integration, feel free to perform the same test cases that we did in the step 5.- Creating the persistence layer - Mybatis.

FYI - All the projects that have been created during this tutorial can be found in my GitHub Repo so please feel free to fork it or even apply adjustments that you believe may improve them. I really hope that this tutorial was found helpful and resourceful.

RESTFul-App-Skeleton
RESTFul-App-RestEasy
RESTFul-App-RestEasy-Mybatis
RESTFul-App-RestEasy-Mybatis-Google-Guice

Acknowledgements


I would like to express my gratitude and appreciation to my good friend Juan Pablo Francisconi (JP). Thanks you very much for sharing your experience with me. Additionally, I would encourage everybody to stop by JP's blog where you will find several entries that I am sure will be of interesest to you.

Thanks for reading, looking forward to having some feedback from you!

References


- Maven
- CRUD Operation and Mapping Relationships
- RestEasy
- RESTEasy MkYong
- RestEasy and Google Guice
- MyBatis
- MyBatis and Google Guice
- MyBatis-Guice 3.2 - Reference Documentation


1 comment:

  1. I really appreciate information shared above. It’s of great help. If someone want to learn Online (Virtual) instructor lead live training in Alfresco , kindly contact us http://www.maxmunus.com/contact
    MaxMunus Offer World Class Virtual Instructor led training on Alfresco. We have industry expert trainer. We provide Training Material and Software Support. MaxMunus has successfully conducted 100000+ trainings in India, USA, UK, Australlia, Switzerland, Qatar, Saudi Arabia, Bangladesh, Bahrain and UAE etc.
    For Demo Contact us:
    Name : Arunkumar U
    Email : arun@maxmunus.com
    Skype id: training_maxmunus
    Contact No.-+91-9738507310
    Company Website –http://www.maxmunus.com


    ReplyDelete