Showing posts with label Rest API. Show all posts
Showing posts with label Rest API. Show all posts

Monday, April 2, 2012

RESTEasy Interceptors



RESTEasy can intercept JAX-RS invocations and route them through listener-like objects called interceptors. There are four interception points on the server side:
  • wrapping around MessageBodyWriter invocations
  • wrapping around MessageBodyReader invocations
  • through pre-processors, which intercept the incoming request before unmarshalling occurs
  • through post-processors, which are invoked immediately after the JAX-RS method finishes 
An interceptor can be very useful to implement crosscutting concerns such as logging and security. Although interceptors are not as powerful as Aspect Oriented Programming (AOP) - (e.g. no type-safety or debugging) , they still offer a quick way to create more compact and maintainable code.

In this article I will describe how to use RESTEasy interceptors to log incoming requests into a REST API.

I am currently using RESTEasy to expose HealthCare web services for consumption by mobiles applications, and there is a need to log transactions for auditing purpose, including for HIPAA compliance.

The first step is to specify in your web.xml file, that you are going to use a new interceptor  - (e.g. named LogInterceptor):
<web-app>
   ...  
   <context-param>
      <param-name>resteasy.providers</param-name>
      <param-value>com.acme.api.rest.service.LogInterceptor</param-value>
   </context-param>
   ...
</web-app>


Then you create the corresponding java class for LogInterceptor:
package com.acme.api.rest.service;
  
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.WebApplicationException;

 
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.ResourceMethod;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.interception.PreProcessInterceptor;
import org.jboss.resteasy.spi.Failure;

...
 
@Provider
@ServerInterceptor
public class LogInterceptor implements PreProcessInterceptor {
 
 // An handler to the Transaction log
 private static final Logger translog = Logger.getLogger(JNDIMessageLoggingHandler.class);

 // Getting the request as a context
 @Context HttpServletRequest request;

 @Override
 public ServerResponse preProcess(HttpRequest arg0, ResourceMethod method) throws Failure, WebApplicationException {
  final TransactionLogEntry transactionLogEntry = new TransactionLogEntry(Long.toString(Thread.currentThread().getId()),
                         System.currentTimeMillis(), 
                         getClientIP(request), 
                         request.getRequestURL().toString());
      translog.info(transactionLogEntry);
 }  
 
}

In this example, the TransactionLogEntry constructor takes :
  • a transaction ID
  • a timestamp
  • the client that initiated the request  (e.g. request.getRemoteAddr()+":"+request.getRemotePort())
  • the request URL
but you can also log additional information such as the body content of POST requests (storing XML or JSON content).

Monday, February 28, 2011

REST-Style Architecture and the Development of Mobile Health Care Applications



Mobile devices offer new ways for users to access health care data and services in a secure and user-friendly environment. These new applications must be easy to create, deploy, test and maintain, and they must rely on a scalable and easily integrated infrastructure.

In the ambulatory health care environment, providers spend the majority of their time in an examination room with patients. Although some clinics have installed personal computers in the exam room for use at the point of care, many physician practices have yet to do so or have no such intention. Reasons for not installing PCs in the exam room include (among others) lack of space, security concerns, and cost. Often, clinics have PCs installed outside of the exam room to be used for encounter documentation or health history research (i.e., reviewing the patient's health records). This physical setup is often satisfactory for providers to complete their documentation needs. Providers often scratch rough notes on paper during an encounter, then dictate or type their notes after the visit has ended. The absence of computers in the exam room, however, is a disadvantage for research activities. Frequently, after listening to the patient's verbal health history, a provider wishes to read past records. If those records are in an electronic format, it is optimal to access those records at the point of care (i.e., in the exam room).

Thus, computer devices that are smaller and more mobile than a PC (e.g., smart phones, PDAs, tablets) would be the optimal hardware choice to access these electronic records. Given that many physicians carry smart phones, such mobile devices would be the ultimate tools to look up patient records.

Since the development of client applications on different mobile platforms requires more time than creating web applications for a handful of browsers, it is important to minimize the complexity of the integration with the back-end services and legacy systems and to try to decouple the development and maintenance of the client- and server-side components.

The Representational State Transfer (REST) architecture is an alternative to SOAP and offers clear advantages over SOAP including lightweight architecture, extensibility, scalability, easy of development, testing, deployment and maintenance.

REST API prototypes can be created in a matter of days and a full functioning set of sophisticated clinical based web services accessible by mobile client applications within few weeks.

In addition to this, REST APIs are particularly suitable for fast and loosely-coupled solution integration such as mobile applications, but can also be used in health care for portal and mash-up applications as well.


Reference:
Andry F., Wan L., Nicholson D., A mobile application accessing patients' health records through a REST API, 4th International Conference on Health Informatics (HEALTHINF 2011), pp 27-32, Rome 2011.







Thursday, June 10, 2010

Response objects and the use of GenericEntity class with RESTEasy

Recently during the implementation of a REST API, I wanted to return a complex response containing a list of objects (Patients). The issue was that the RESTEasy build-in JAXB MessageBodyWriter could not directly handle lists of JAXB objects (Java has trouble obtaining generic type information at runtime).

I was recently in a situation where I had to create a complex response to a HTTP POST for my REST API. I am using JAXB /JSON support from RESTEasy.

I found some element of answer in the book "RESTFul Java with JAX-RS" from Bill Burke (pp 102). However the code snippet had a couple of errors:

  • the GenericEntity object cannot be passed to the Response.ok() method directly (a ResponseBuilder is required).

  • references to GenericEntity needs to be parameterized.

My use case is a little more complex than in the book. I am receiving a user-name and password from a POST (e.g. a form submit). I then perform the authentication and returns a list of Patient objects in a JSON/GZIP compressed format (instead a list of Customer objects) together with an authentication token.





The resulting code looks like this:


   @POST
   @Path("/token")
   @Consumes("application/x-www-form-urlencoded")
   @Produces("application/json")
   @GZIP
   public Response getPatientsWithToken(@FormParam("username") String username, @FormParam("password") String password) {
  
        Login login = new Login(username, password);
        // ... perform authentication here ....
    
        // Build the returning patient list
        List<Patient> returnList = new ArrayList<Patient>();
        returnList.addAll(patients.values());
        Collections.sort(returnList);
      
        GenericEntity<List<Patient>> entity = new GenericEntity<List<Patient>>(returnList){};
      
        // Create the response
        ResponseBuilder builder = Response.ok(entity);
        return builder.build();
   }



Of course you will have to import the following classes as well:


import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

Tuesday, June 1, 2010

JAXB-JSON Rest API using RESTEasy on JBoss EAP

In this second part of my evaluation of JBoss RESTEasy, I focus on adapting the JAXB-JSON samples provided by RESTEasy for JBoss Enterprise Application Platform 5.0.1.

Earlier I find myself to adapt the maven POM file to have the proper dependencies for the Twitter RESTEasy client.

Initially this simple JAXB-JSON sample had been designed to run on Jetty Web Server which run fine out-of-the box. However I had to make some modifications to the original project structure to have the code running as a simple eclipse project that can be deploy on JBoss EAP 5.0.X from RedHat (this will also work on Jboss community edition).

The new project (eclipse) structure looks as below:





Notice that I have also moved the code for both packages:  org.jboss.resteasy.annotations.providers.jaxb.json
org.jboss.resteasy.plugins.providers.jaxb.json
at the root of my project, since I am not using the remaining code of the example.

I also made some additional adaptations for JBoss to some of the project files including:
  • pom.xml file
  • web.xml
I also added a small test suite to test the REST API operations using JUnit which will work after the first deployment (I run JBoss locally on http://localhost:8080).

By the way, make sure you have src/main/resources/META-INF/services/javax.ws.rs.ext.Providers included in your project.









Here is the content of my new pom.xml file for JBoss:

<?xml version="1.0" encoding="UTF-8"?>
<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>
    <groupId>org.jboss.resteasy.examples</groupId>
    <artifactId>jaxb-json</artifactId>
    <version>0.1.0</version>
    <packaging>war</packaging>
    <name/>
    <description/>

    <repositories>
        <repository>
            <id>java.net</id>
            <url>http://download.java.net/maven/1</url>
            <layout>legacy</layout>
        </repository>
        <repository>
            <id>maven repo</id>
            <name>maven repo</name>
            <url>http://repo1.maven.org/maven2/</url>
        </repository>
        <!-- For resteasy -->
        <repository>
            <id>jboss</id>
            <name>jboss repo</name>
            <url>http://repository.jboss.org/maven2</url>
        </repository>
    </repositories>
    <dependencies>
    
        <!-- core library -->
        
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>1.2.1.GA</version>
            <!-- filter out unwanted jars -->
            <exclusions>
                <exclusion>
                    <groupId>commons-httpclient</groupId>
                    <artifactId>commons-httpclient</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>tjws</groupId>
                    <artifactId>webserver</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <!-- optional modules -->
        
      <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jettison-provider</artifactId>
            <version>1.2.1.GA</version>
        </dependency>
        
        <!-- modules already provided by Java 6.0 -->
         
   <dependency>
       <groupId>javax.xml.bind</groupId>  
       <artifactId>jaxb-api</artifactId>
       <version>2.1</version>
       <scope>provided</scope>
   </dependency>
   
    <dependency>
         <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.1</version>
                 <scope>test</scope>
             </dependency>
     
    </dependencies>

    <build>
        <finalName>jaxb-json</finalName>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jboss-maven-plugin</artifactId>
                <version>1.4</version>
                <configuration>
                    <jbossHome>C:\JBoss\EnterprisePlatform-5.0.0.GA\jboss-as</jbossHome>
             <contextPath>/</contextPath>
             <serverName>default</serverName>
             <fileName>target/jaxb-json.war</fileName>
          </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


I modified the web.xml for the URL looks more simple by removing the mapping to reasteasy:

<?xml version="1.0"?>
<!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>

   <context-param>
      <param-name>javax.ws.rs.Application</param-name>
      <param-value>org.jboss.resteasy.examples.service.LibraryApplication</param-value>
   </context-param>

   <context-param>
      <param-name>resteasy.servlet.mapping.prefix</param-name>
      <param-value>/</param-value>
   </context-param>

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

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

   <servlet-mapping>
      <servlet-name>Resteasy</servlet-name>
      <url-pattern>/</url-pattern>
   </servlet-mapping>

</web-app>

The JUnit code looks like this:

package org.jboss.resteasy.examples.test;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
  
public class LibraryTest extends TestCase
{
    /**
     * Create the test case
     *
     * @param testName name of the test case
     */
    public LibraryTest( String testName )
    {
        super( testName );
    }

    /**
     * @return the suite of tests being tested
     */
    public static Test suite()
    {
        return new TestSuite( LibraryTest.class );
    }

      
    /**
     * Testing the Library REST API
     */
    
    public void testGetMapped()  
    {
     validateRESTCall("GET", "http://localhost:8080/jaxb-json/library/books/mapped");
     assertTrue( true );
    }
    

    public void testGetBadger()  
    {
     validateRESTCall("GET", "http://localhost:8080/jaxb-json/library/books/badger");
     assertTrue( true );
    }
    
    private void validateRESTCall(String method, String url) {
     
     try {
         System.out.println("*** "+method);
         URL resURL = new URL(url);
         System.out.println("URL: " + url.toString());
         HttpURLConnection connection = (HttpURLConnection) resURL.openConnection(); 
         connection.setRequestMethod(method);
         System.out.println("Content-Type: " + connection.getContentType());
         
         BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
 
         String line = reader.readLine();
         while (line != null)
         {
            System.out.println(line);
            line = reader.readLine();
         }
         Assert.assertEquals(HttpURLConnection.HTTP_OK, connection.getResponseCode());
         connection.disconnect();
     } catch (Exception err) { 
         System.out.print("Error in VHRResourceTest.validateRESTCall : " + err); 
        };
    }
}

To build I am using Maven (I recommend to install the maven eclipse plugin) with the goals mvn clean install compile package. Make sure also that before that you do a mvn eclipse:eclipse to update the dependencies in your project.

To deploy jaxb-json.war file from eclipse (so I don't have to manually copy the war file from the target folder), I have installed the JBoss eclipse plugin. As a result I can make it deployable (accessible by a right-click) and it appear in the Eclipse JBoss server view:


The REST API JSON Library resources are then accessible directly on a browser via http://localhost:8080/jaxb-json/library/books/mapped or http://localhost:8080/jaxb-json/library/books/badger.

If you want to compress your response, RESTEasy provides GZIP Compression/Decompression support using a very simple @GZIP annotation:

   @GET
   @Path("books/mapped")
   @Produces("application/json")
   @GZIP
   public BookListing getBooksMapped()
   {
      return getListing();
   }

Just import the following class:

import org.jboss.resteasy.annotations.GZIP;

Overall the adaptation from Jetty to JBoss was easy and the documentation very clear.

Additional discussions, recommendations and information can be found on the JBoss Community.

For an example of using REST architecture for Mobile Applications (HealthCare) see this post.