Showing posts with label rest. Show all posts
Showing posts with label rest. Show all posts

Posting data to REST service in PhoneGap/Cordova

In the Consuming REST service from PhoneGap/Cordova entry i talked about how you can consume a REST service in PhoneGap/Cordova application using JQuery. In that application i was making a HTTP GET request to get data from REST service, i wanted to figure out how to make a HTTP POST call to REST service to create a new contact. You can download the index.html for my application from here This is screen shot of how my form looks like
This is screen shot of success message that i get if the contact insertion is successful

I followed these steps to build the CordovaManageContact application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i changed the index.html file for my application to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Manage Contact</title>
    <script type="text/javascript" 
    charset="utf-8" src="cordova-1.7.0.js"></script>
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script type="text/javascript" charset="utf-8">
      $(document).ready(function(){
        $("#submit").click(insertContact);
      });
      function insertContact(){
        console.log("Entering insertContact()");
        $.post("http://192.168.1.101:9000/ManageContact/rest/contact",
        $("#insertContact :input").serializeArray(), 
        function(json){
          if(json== null || json == 'undefined')
            alert("Insert failed");
          else
            alert("Insert successful");
        });
        return false;
      }
    </script>
    </head>
    <body>
      <h3>Insert Contact</h3>
      <form id="insertContact">
        <table>
          <tr>
            <td>Contact Id</td>
            <td><input type="text" name="contactId" /></td>
          </tr>
          <tr>
            <td>First Name</td>
            <td><input type="text" name="firstName" /></td>
          </tr>
          <tr>
            <td>Last Name</td>
            <td><input type="text" name="lastName" /></td>
          </tr>
          <tr>
            <td>Email</td>
            <td><input type="text" name="email" /></td>
          </tr>
          <tr>
            <td><input type="submit" 
      id="submit" name="submit" value="Submit" /></td>
          </tr>
        </table>
      </form>
    </body>
    </html>
    
    When the user clicks on submit button control goes to insertContact() method., In this method i am using jQuery $.post() call to submit the form to http://192.168.1.101:9000/ManageContact/rest/contact URL. I am using jQuery to collect all the values entered by the user into form and encode them by calling $("#insertContact :input").serializeArray() method. After the post request control goes to the anonymous function which is third parameter of the $.post() method. In that method i am checking if i got response if yes that means insert was successful if not that means insert failed, that is because my REST service is structured not to send anything back in case of insert failure.

Consuming REST service from PhoneGap/Cordova

In the Using JPA in REST web application deployed in Jetty entry i blogged about how to build a ManageContact JPA service which will allow me to perform CRUD operations on CONTACT table using a REST service. I wanted to figure out how i can consume this service from PhoneGap/Cordova application so i built this sample application which will call http://localhost:9000/ManageContact/rest/contact rest service, read the contact list returned in XML format, parse it and display contact list to the user, you can download the sample application from here This screen shot displays the list of contacts that i got from the REST service I followed these steps to build the CordovaManageContact application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i changed the index.html file for my application to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Device Properties Example</title>
    <script type="text/javascript" charset="utf-8" 
    src="cordova-1.7.0.js"></script>
    <script src="js/jquery.js"></script>
    
    <script type="text/javascript" charset="utf-8">
      $(document).ready(function(){
        $("#getContactBtn").click(getContactList);
      });
      function onDeviceReady() {
        console.log("Entering index.html.onDeviceReady");
        getContactList();
        console.log("Exiting index.html.onDeviceReady");
      }
      function getContactList(){ 
        console.log("Entering getContactList()");
        $.ajax({
          url : "http://192.168.1.101:9000/ManageContact/rest/contact",
          dataType:"xml",
          cache: false,
          error:function (xhr, ajaxOptions, thrownError){
            debugger;
                    alert(xhr.statusText);
                    alert(thrownError);
                },
          success : function(xml) {
            console.log("Entering getContactList.success()");
          
            $(xml).find("contact").each(function() {
              var html = '<li>' + $(this).find("firstName").text() 
              + ' ' + $(this).find("lastName").text() +'</li>';
              $('#contactList').append(html);
            });
            console.log("Exiting getContactList.success()");
          } 
        });
        console.log("Exiting getContactList()");
      }
    </script>
    </head>
    <body>
      <h3>Contact List</h3>
      <button id="getContactBtn">Get Contact</button>
      <ul id="contactList"></ul>
    </body>
    </html>
    
    When i click on the Get Contact button the getContactList() method gets called, it uses the jquery $.ajax() method to make a call and once the result is returned, it uses logic in the success method to parse the xml and get all the contact records and adds each one of them as list item in the contactList list. I have the REST service running on my machine along with the Android emulator which has the PhoneGap application but when i tried to access the service at http://localhost/ManageContact/rest/contact,http://127.0.0.1/ManageContact/rest/contact it did not work. I tried to map the ip address 192.168.1.101 to demohost.com in my host file but Android did not understand that mapping either. I had to use ipconfig command to figure out the ip address of the machine and then use it.

Enabling Request and Response logging in Jersey

I have been using Jersey as JAX-RS reference implementation for building REST services for last few days. And i wanted to enable logging so that it makes debugging easier. It seems that Jersey allows us to enable Request and Response logging and once you enable that it starts printing request and response info. In order to use that all you have to do is change web.xml to add LoggingFilter on both request and response like this

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>

    <init-param>
         <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
         <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
     </init-param>
     <init-param>
         <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
         <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
     </init-param>
   
 <init-param>
      <param-name>javax.ws.rs.Application</param-name>
      <param-value>com.webspherenotes.rest.ContactApplication</param-value>
    </init-param>
  
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey Web Application</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>

</web-app>
The com.sun.jersey.api.container.filter.LoggingFilter can be used to enable loggin on either request or response or both as i am doing in this case. Now if i hit a REST service by making GET call to http://localhost:9000/ManageContact/rest/contact/5 then the service returns JSON reso If you look into the generated log you can see both the request and response being printed like this

Securing REST service using annotations

In the Security REST service using web.xml entry i talked about how you can protect a REST service by adding security-constraint element in the web.xml. But JAXRS provides annotations that would give you more granular control over the REST services. I wanted to try that feature so i changed the same ManageContactApp to use the javax.annotation.security annotations. You can download the sample application from here When the user tries to insert a new record he will get prompted for basic authentication like this I followed these steps to build the sample application
  1. First i did download the basic ManageContactApp.zip that provides REST interface and i tested it to make sure that it works
  2. Next i used the instructions in Securing web application deployed in Jetty to make changes in Maven build file(pom.xml) to enable loginService in Jetty
    
    <build>
      <finalName>JettySecurity</finalName>
      <plugins>
        <plugin>
          <groupId>org.mortbay.jetty</groupId>
          <artifactId>jetty-maven-plugin</artifactId>
          <version>7.4.5.v20110725</version>
          <configuration>
            <scanIntervalSeconds>10</scanIntervalSeconds>
            <webAppConfig>
              <contextPath>/JettySecurity</contextPath>
            </webAppConfig>
            <loginServices>
              <loginService implementation="org.eclipse.jetty.security.HashLoginService">
                <name>Default</name>
                <config>${basedir}/src/main/resources/realm.properties</config>
              </loginService>
            </loginServices> 
            <connectors>
              <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
                <port>9000</port>
                <maxIdleTime>60000</maxIdleTime>
              </connector>
            </connectors>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  3. Next create realm.properties file in ${basedir}/src/main/resources directory which looks like this
    
    guest:guest
    admin:admin,ADMIN
    
    This file has only 2 users first is guest and second is admin the admin user has ADMIN role.
  4. Next change the web.xml file to declare the ADMIN role and define BASIC authentication scheme for the web application like this.
    
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
      <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>
    	com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
          <param-name>javax.ws.rs.Application</param-name>
          <param-value>com.webspherenotes.rest.ContactApplication</param-value>
        </init-param>
    
        <init-param>
          <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
          <param-value>
    	  com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory</param-value>
        </init-param>
       
     <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/rest/*</url-pattern>
      </servlet-mapping>
    
    
      <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Default</realm-name>
      </login-config>
    
      <security-role>
        <role-name>ADMIN</role-name>
      </security-role>
    </web-app>
    
    By default the Jersey implementation does not look for security annotations in your REST service, in order for that to work you must set value of com.sun.jersey.spi.container.ResourceFilters init parameter to com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory this filter takes care of parsing and understanding PermitAll, RolesAllowed and DenyAll annotations
  5. The last step is to change the ContactService.java to use the security related annotations at individual class and method level
    
    package com.webspherenotes.rest;
    
    import java.util.List;
    
    import javax.annotation.security.PermitAll;
    import javax.annotation.security.RolesAllowed;
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Query;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.DELETE;
    import javax.ws.rs.FormParam;
    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 javax.ws.rs.core.MediaType;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    @Path("/contact")
    
    public class ContactService {
    
      Logger logger = LoggerFactory.getLogger(ContactService.class);
      
      EntityManagerFactory entityManagerFactory;
      public ContactService(EntityManagerFactory entityManagerFactory){
        this.entityManagerFactory=entityManagerFactory;
      }
    
      @GET
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      public List getContactList() {
        logger.debug("Entering ContactService.getContactList()");
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Query q = entityManager.createQuery("SELECT x from Contact x");
        logger.debug("Exiting ContactService.getContactList()");
    
        return (List) q.getResultList();
      }
    
      @GET
      @Path("/{contactId}")
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      public Contact getContact(@PathParam("contactId") int contactId) {
        logger.debug("Entering ContactService.getContact() contactId" + contactId);
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = entityManager.find(Contact.class, contactId);
        logger.debug("Exiting ContactService.getContact()" );
    
        return contact;
      }
      
      @POST
      @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      @RolesAllowed("ADMIN")
      public Contact insertContact(@FormParam("contactId") int contactId,
          @FormParam("firstName") String firstName,
          @FormParam("lastName") String lastName,
          @FormParam("email") String email) {
        logger.debug("Entering ContactService.insertContact()");
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = new Contact();
        contact.setContactId(contactId);
        contact.setFirstName(firstName);
        contact.setLastName(lastName);
        contact.setEmail(email);
        try{
        entityManager.getTransaction().begin();
        
        entityManager.persist(contact);
        entityManager.getTransaction().commit();
        }catch(Throwable t){
          if(entityManager.getTransaction().isActive())
            entityManager.getTransaction().rollback();
          contact = null;
        }finally{
          entityManager.close();
        }
        logger.debug("Exiting ContactService.insertContact()");
        return contact;
      }
    
    
      @PUT
      @Path("/{contactId}")
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
      @RolesAllowed("ADMIN")
      public Contact updateContact(@PathParam("contactId") int contactId,
          @FormParam("firstName") String firstName,
          @FormParam("lastName") String lastName,
          @FormParam("email") String email) {
        logger.debug("Entering ContactService.update() contactId" + contactId);
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = new Contact();
        contact.setContactId(contactId);
        contact.setFirstName(firstName);
        contact.setLastName(lastName);
        contact.setEmail(email);
        try{
        entityManager.getTransaction().begin();
        entityManager.merge(contact);
        entityManager.getTransaction().commit();
        }catch(Throwable t){
          if(entityManager.getTransaction().isActive())
            entityManager.getTransaction().rollback();
          contact = null;
        }finally{
          entityManager.close();
        }
        logger.debug("Exiting ContactService.updateContact()");
    
        return contact;
      }
    
      @DELETE
      @Path("/{contactId}")
      @RolesAllowed("ADMIN")
      public void deleteContact(@PathParam("contactId") int contactId) {
        logger.debug("Entering ContactService.deleteContact() contactId " + contactId);
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try{
          entityManager.getTransaction().begin();
          Contact contact = entityManager.find(Contact.class, contactId);
          logger.debug("remove contact " + contact);
          entityManager.remove(contact);
          logger.debug("After removing " + contact);
          entityManager.getTransaction().commit();
          }catch(Throwable t){
            if(entityManager.getTransaction().isActive())
              entityManager.getTransaction().rollback();
    
          }finally{
            entityManager.close();
          }
        logger.debug("Exiting ContactService.deleteContact()");
      }
    
    }
    
    By default all the methods are accessible to user. But i did add @RolesAllowed("ADMIN") to insertContact(), updateContact() and deleteContact() method to say that only users who have admin rights can access these methods.

Security REST service using web.xml

In the Using JPA in REST web application deployed in Jetty entry i talked about how to create a REST service which uses JPA to interact with database. This service allows you to list, insert, update and delete records from CONTACT table. When you create a service that allows you to update your back end you might want to protect it so that only authorized user can update the database. I wanted to figure out how to protect the service so that every user can get list of contacts but only ADMIN user is able to modify the contacts by inserting, updating and deleting them. YOu can download the sample application that i developed from here When the user tries to insert a new record he will get prompted for basic authentication like this I followed these steps to build the sample application
  1. First i did download the basic ManageContactApp.zip that provides REST interface and i tested it to make sure that it works
  2. Next i used the instructions in Securing web application deployed in Jetty to make changes in Maven build file(pom.xml) to enable loginService in Jetty
    
    <build>
      <finalName>JettySecurity</finalName>
      <plugins>
        <plugin>
          <groupId>org.mortbay.jetty</groupId>
          <artifactId>jetty-maven-plugin</artifactId>
          <version>7.4.5.v20110725</version>
          <configuration>
            <scanIntervalSeconds>10</scanIntervalSeconds>
            <webAppConfig>
              <contextPath>/JettySecurity</contextPath>
            </webAppConfig>
            <loginServices>
              <loginService implementation="org.eclipse.jetty.security.HashLoginService">
                <name>Default</name>
                <config>${basedir}/src/main/resources/realm.properties</config>
              </loginService>
            </loginServices> 
            <connectors>
              <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
                <port>9000</port>
                <maxIdleTime>60000</maxIdleTime>
              </connector>
            </connectors>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  3. Next create realm.properties file in ${basedir}/src/main/resources directory which looks like this
    
    guest:guest
    admin:admin,ADMIN
    
    This file has only 2 users first is guest and second is admin the admin user has ADMIN role.
  4. The next step is to make changes in web.xml to protect the appropriate HTTP method calls in web.xml like this
    
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
      <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>
    	com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param> 
              <param-name>javax.ws.rs.Application</param-name> 
              <param-value>
    		  com.webspherenotes.rest.ContactApplication</param-value> 
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/rest/*</url-pattern>
      </servlet-mapping>
    
    
      <security-constraint>
        <web-resource-collection>
          <web-resource-name>Create Contact</web-resource-name>
          <url-pattern>/rest/*</url-pattern>
          <http-method>POST</http-method>
          <http-method>PUT</http-method>
          <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint>
          <role-name>ADMIN</role-name>
        </auth-constraint>
      </security-constraint>
    
      <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Default</realm-name>
      </login-config>
    
      <security-role>
        <role-name>ADMIN</role-name>
      </security-role>
    
    </web-app>
    
    The most important change in web.xml is defining the security constraints for the /rest URL, which is the base URL for the REST service. The security constraint says that only allow those users who have ADMIN role access to POST, PUT, DELETE HTTP methods on this URL. The login-config element says that use Basic authentication
Now run the web application by executing mvn jetty:run and you will notice that you can get list of contacts but when you try to either insert a new contact or delete existing contact then you will get prompted for userid and password

Using JPA in REST web application deployed in Jetty

I wanted to figure out how to use Hibernate JPA in a web application deployed in Jetty server, so i built this sample Contact REST service that uses JPA to perform CRUD operations on the CONTACT table and expose those operations using REST, you can download it from here I followed these steps to build the REST service.
  1. First i did create a Contact.java class which is a Entity class representing CONTACT table
    
    package com.webspherenotes.rest;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @Entity
    @XmlRootElement
    public class Contact {
     @Id
     private int contactId;
    
     private String firstName;
     private String lastName;
     private String email;
     
     public int getContactId() {
      return contactId;
     }
     public void setContactId(int contactId) {
      this.contactId = contactId;
     }
     public String getFirstName() {
      return firstName;
     }
     public void setFirstName(String firstName) {
      this.firstName = firstName;
     }
     public String getLastName() {
      return lastName;
     }
     public void setLastName(String lastName) {
      this.lastName = lastName;
     }
     public String getEmail() {
      return email;
     }
     public void setEmail(String email) {
      this.email = email;
     }
     
     @Override
     public String toString() {
      return "Contact [contactId=" + contactId + ", firstName=" + firstName
        + ", lastName=" + lastName + ", email=" + email + "]";
     }
    }
    
    The contactId field represents the primary key for contact table. The Contact class has Entity annotation that maps it to table in database.
  2. Then i did create peristence.xml file which acts as deployment descriptor for the JPA in the META-INF folder of the application like this
    
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0"
     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">
     <persistence-unit name="MyJPAJAXRS">
      <class>com.webspherenotes.rest.Contact</class>
      <properties>
       <property name="javax.persistence.jdbc.driver" 
       value="org.apache.derby.jdbc.ClientDriver" />
       <property name="javax.persistence.jdbc.url" 
       value="jdbc:derby://localhost:1527/C:/data/contact;create=true" />
       <property name="javax.persistence.jdbc.user" value="dbadmin" />
       <property name="javax.persistence.jdbc.password" value="dbadmin" />
      </properties>
     </persistence-unit>
    </persistence>
    
    The persistence.xml file is using JDBC driver properties for connecting to Apache Derby database.
  3. Create ContactApplication.java class which be the Application class for this REST application
    
    package com.webspherenotes.rest;
    javascript:void(0);
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    @ApplicationPath("rest")
    public class ContactApplication extends Application{
      Logger logger = LoggerFactory.getLogger(ContactApplication.class);
    
      @Override
      public Set<Object> getSingletons() {
        logger.debug("Entering ContactApplication.getSingletons()");
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("MyJPAJAXRS");
        ContactService contactService = new ContactService(entityManagerFactory);
        Set<Object> singletons = new HashSet<Object>();
        singletons.add(contactService);
        logger.debug("Exiting ContactApplication.getSingletons()");
        return singletons;
      }
    
    }
    
    The ContactApplication class overrides getSingletons() method so that it can create single instance of ContactService, which will be responsible for handling all the requests for ContactService. Since Jersey in not JEE 6 container i am responsible for creating object of EntityManagerFactory. After creating object of EntityManagerFactory i am injecting it into the ContactService class.
  4. The last step is to create ContactService.java, which exposes the REST operations
    
    package com.webspherenotes.rest;
    
    import java.util.List;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Query;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.DELETE;
    import javax.ws.rs.FormParam;
    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 javax.ws.rs.core.MediaType;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    @Path("/contact")
    public class ContactService {
    
      Logger logger = LoggerFactory.getLogger(ContactService.class);
      
      EntityManagerFactory entityManagerFactory;
      public ContactService(EntityManagerFactory entityManagerFactory){
        this.entityManagerFactory=entityManagerFactory;
      }
    
      @GET
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      public List getContactList() {
        logger.debug("Entering ContactService.getContactList()");
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Query q = entityManager.createQuery("SELECT x from Contact x");
        logger.debug("Exiting ContactService.getContactList()");
    
        return (List) q.getResultList();
      }
    
      @GET
      @Path("/{contactId}")
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      public Contact getContact(@PathParam("contactId") int contactId) {
        logger.debug("Entering ContactService.getContact() contactId" + contactId);
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = entityManager.find(Contact.class, contactId);
        logger.debug("Exiting ContactService.getContact()" );
    
        return contact;
      }
      
      @POST
      @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      public Contact insertContact(@FormParam("contactId") int contactId,
          @FormParam("firstName") String firstName,
          @FormParam("lastName") String lastName,
          @FormParam("email") String email) {
        logger.debug("Entering ContactService.insertContact()");
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = new Contact();
        contact.setContactId(contactId);
        contact.setFirstName(firstName);
        contact.setLastName(lastName);
        contact.setEmail(email);
        try{
        entityManager.getTransaction().begin();
        
        entityManager.persist(contact);
        entityManager.getTransaction().commit();
        }catch(Throwable t){
          if(entityManager.getTransaction().isActive())
            entityManager.getTransaction().rollback();
          contact = null;
        }finally{
          entityManager.close();
        }
        logger.debug("Exiting ContactService.insertContact()");
        return contact;
      }
    
    
      @PUT
      @Path("/{contactId}")
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
      public Contact updateContact(@PathParam("contactId") int contactId,
          @FormParam("firstName") String firstName,
          @FormParam("lastName") String lastName,
          @FormParam("email") String email) {
        logger.debug("Entering ContactService.update() contactId" + contactId);
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = new Contact();
        contact.setContactId(contactId);
        contact.setFirstName(firstName);
        contact.setLastName(lastName);
        contact.setEmail(email);
        try{
        entityManager.getTransaction().begin();
        entityManager.merge(contact);
        entityManager.getTransaction().commit();
        }catch(Throwable t){
          if(entityManager.getTransaction().isActive())
            entityManager.getTransaction().rollback();
          contact = null;
        }finally{
          entityManager.close();
        }
        logger.debug("Exiting ContactService.updateContact()");
    
        return contact;
      }
    
      @DELETE
      @Path("/{contactId}")
    
      public void deleteContact(@PathParam("contactId") int contactId) {
        logger.debug("Entering ContactService.deleteContact() contactId " + contactId);
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try{
          entityManager.getTransaction().begin();
          Contact contact = entityManager.find(Contact.class, contactId);
          logger.debug("remove contact " + contact);
          entityManager.remove(contact);
          logger.debug("After removing " + contact);
          entityManager.getTransaction().commit();
          }catch(Throwable t){
            if(entityManager.getTransaction().isActive())
              entityManager.getTransaction().rollback();
    
          }finally{
            entityManager.close();
          }
        logger.debug("Exiting ContactService.deleteContact()");
      }
    
    }
    
    The ContactService class exposes CRUD operations on the CONTACT table using REST

Returning a binary file from REST service

You might want to return a binary file such as image or pdf from a REST service, if thats the case then JAX-RS provides you with 3 options. I wanted to try those options to i did create a sample application, that you can download from here. I did create a HelloBinaryService.java file which is a resource class that has 3 methods in each of these methods i am reading a static image file from c:/temp and returning it

package com.webspherenotes.rest;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.StreamingOutput;

@Path("/hellobinary")
public class HelloBinaryService {

  @GET
  @Path("/file")
  @Produces("image/png")
  public File getFile(){
    File samplePDF = new File("c:/temp/distributedmap.png");
    return samplePDF;
  }

  @GET
  @Path("/fileis")
  @Produces("image/png")
  public InputStream getFileInputStream()throws Exception{
    FileInputStream fileIs = new FileInputStream("c:/temp/distributedmap.png");
    return fileIs;
  }

  @GET
  @Path("/fileso")
  @Produces("image/png")
  public StreamingOutput getFileStreamingOutput() throws Exception{
    
    return new StreamingOutput() {
      
      @Override
      public void write(OutputStream outputStream) throws IOException,
          WebApplicationException {
        FileInputStream inputStream = new FileInputStream("c:/temp/distributedmap.png");
        int nextByte = 0;
        while((nextByte  = inputStream.read()) != -1 ){
          outputStream.write(nextByte);
        }
        outputStream.flush();
        outputStream.close();
        inputStream.close();
      }
    };
  }
  
}
The first and easiest option would be to return a object of java.io.File directly and the JAX-RS will figure out how to read the file and return binary content of the file, but you cannot use this option if your returning a file that is not stored on the file system. Second option would be to return object of InputStream, in that case the InputStream could be pointing to file on local system or file stored in database or something like that, in this case JAX-RS container will figure out how to return bytes from InputStream and return it The last option would be to return object of StreamingOutput, this gives you most flexibility in this case JAX-RS container will give you control at the time of writing body of the message, this is little more work for you since you have to take care of reading the bytes from InputStream and write into output stream but then you get chance to compress the content,....

Createing Hello JAXRS service for deployment using Sun Jersey

If you want to build a REST service and deploy it in one of the Java EE 5 compliant containers for example WebSphere APplication Server 7.0, then you will have to use one of the external JAXRS containers such as Jersey, which is very popular among REST service developers.

I wanted to try this option out so i built a HelloJersey application that you can download from here. I followed these steps for building my sample application

  • First create a Dynamic Web APplication project called HelloJersey

  • Download following .jar files and add it to the WEB-INF/lib folder of your web application

    1. asm-3.1.jar

    2. jersey-core-1.5.jar

    3. jersey-server-1.5.jar

    4. jsr311-api-1.1.1.jar




  • Next create a HelloJerseyRESTService.java file like this

    package com.webspherenotes.rest;

    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.QueryParam;
    import javax.ws.rs.core.MediaType;

    @Path("/hellojersey")
    public class HelloJerseyRESTService {

    @GET
    @Produces(MediaType.TEXT_HTML)
    public String sayHello(@QueryParam("name") String name ){
    return "Hello " + name;
    }
    }

    The HelloJerseyRESTService only handles GET request and returns HTML markup


  • Next declare the Jersey servlet in the web.xml and create servlet mapping

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.5"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>DynaCacheSample1</display-name>
    <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.webspherenotes.rest</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    </web-app>



  • After you deploy the application in container you should see that when the server is starting it will scan all the resource and in our case it should find only com.webspherenotes.rest.HelloJerseyRESTService

    [10/16/11 17:17:47:250 PDT] 0000000e ApplicationMg I WSVR0228I: User initiated module stop operation request completed on Module, HelloJersey.war, of application, DynaCacheSample1EAR
    [10/16/11 17:17:49:932 PDT] 0000000e ApplicationMg I WSVR0225I: User initiated module start operation requested on Module, HelloJersey.war, of application, DynaCacheSample1EAR
    [10/16/11 17:17:50:466 PDT] 0000000e webapp I com.ibm.ws.webcontainer.webapp.WebGroupImpl WebGroup SRVE0169I: Loading Web Module: HelloJersey.
    [10/16/11 17:17:50:521 PDT] 0000000e WASSessionCor I SessionContextRegistry getSessionContext SESN0176I: Will create a new session context for application key default_host/HelloJersey

    [10/16/11 17:17:50:602 PDT] 0000000e PackagesResou I Scanning for root resource and provider classes in the packages:
    com.webspherenotes.rest
    [10/16/11 17:17:50:666 PDT] 0000000e ScanningResou I Root resource classes found:
    class com.webspherenotes.rest.HelloJerseyRESTService

    [10/16/11 17:17:50:667 PDT] 0000000e ScanningResou I No provider classes found.
    [10/16/11 17:17:50:850 PDT] 0000000e WebApplicatio I Initiating Jersey application, version 'Jersey: 1.5 01/14/2011 12:36 PM'
    [10/16/11 17:17:51:816 PDT] 0000000e servlet I com.ibm.ws.webcontainer.servlet.ServletWrapper init SRVE0242I: [DynaCacheSample1EAR] [/HelloJersey] [Jersey REST Service]: Initialization successful.
    [10/16/11 17:17:51:817 PDT] 0000000e webcontainer I com.ibm.ws.wswebcontainer.VirtualHost addWebApplication SRVE0250I: Web Module HelloJersey has been bound to default_host[*:9080,*:80,*:9443,*:5060,*:5061,*:443].
    [10/16/11 17:17:51:824 PDT] 0000000e ApplicationMg I WSVR0226I: User initiated module start operation request completed on Module, HelloJersey.war, of application, DynaCacheSample1EAR
    [10/16/11 17:17:51:825 PDT] 0000000e AppBinaryProc I ADMA7021I: Distribution of application DynaCacheSample1EAR completed successfully.




You can test the service by either directly going to the http://localhost:9080/HelloJersey/rest/hellojersey?name=sunil URL or using the REST client