Showing posts with label REST. Show all posts
Showing posts with label REST. Show all posts

Wednesday, June 13, 2012

How to create a simple CXF based JAX-RS Client

 
In my previous blog post, I did explain how to create a REST API using CXF and Spring.

This time I would like to describe how to quickly create a client that can call the REST API while reusing the resources POJO classes,
so the un-marshalling is done by JAXB.

For this I am using the CXF WebClient which is very simple to use:

  • import WebClient from org.apache.cxf.jaxrs.client.WebClient
  • import your POJOs
  • import a JABX provider (I am using Jackson JAXB JSON provider)

 Here is a very simple standalone program that does exactly this (make sure your REST API has been deployed locally):
 
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.apache.cxf.jaxrs.client.WebClient;
import java.util.ArrayList;
import java.util.List;

public class App 
{
    public static void main( String[] args )  throws Exception { 
      
     List<Object> providers = new ArrayList<Object>();
     providers.add( new JacksonJaxbJsonProvider() );
   
     WebClient client = WebClient.create("http://localhost:8080/poc_restapi_cxf/api", providers);
     client = client.accept("application/json").type("application/json").path("/order").query("id", "1");
   
     Order order = client.get(Order.class);
     System.out.println("Order:" + order.getCustomerName());
  
    }
}
With this very simple client program, the response to your request (JSON) is automatically unmarshalled to a Java object. 

If you are using maven, make sure you have the correct dependencies in your pom file:
    <dependency>
       <groupId>org.apache.cxf</groupId>
       <artifactId>cxf-rt-transports-http</artifactId>
       <version>${cxf.version}</version>
       <scope>provided</scope>
    </dependency>
   
    <dependency>
       <groupId>org.apache.cxf</groupId>
       <artifactId>cxf-rt-rs-extension-search</artifactId>
       <version>${cxf.version}</version>
       <scope>provided</scope>
    </dependency>
     
    <dependency>
       <groupId>org.apache.cxf</groupId>
       <artifactId>cxf-rt-rs-extension-providers</artifactId>
       <version>${cxf.version}</version>
       <scope>provided</scope>
    </dependency>
      
    <dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
       <artifactId>jackson-jaxrs-json-provider</artifactId>
       <version>2.0.2</version>
    </dependency> 

Tuesday, May 29, 2012

REST API with CXF and Spring

 

For the past couple of years, I have developed REST APIs using RESTEasy since the target deployment platform was JBOSS. More recently I have considered using Apache CXF instead. One reason is that we wanted to use a single technology to create web services (we are using CXF for our SOAP based web services).

In this article I will show how to create a very simple REST web service using CXF and Spring and describe how to deploy it on JBOSS AS 7.0.

I have found several tutorials describing how to create CXF based REST web services but none with Spring  and working when deployed on JBOSS AS 7.0. Part of the proof of concept (POC) code sample I am describing in this article come from Sandeep Bhandari article "REST Web Service Using CXF - Beginner's Tutorial" which I enriched with Spring integration and deployment configuration for JBOSS AS 7.0.




In my POC example, there is not much differences for the POJO classes representing the order resources (I made them a little bit simple):








class Order
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "order")
public class Order {
 
    private String itemName;
    private int quantity;
    private String customerName;
    
    @XmlElement
    public String getItemName() { return itemName; }
    
    public void setItemName(String itemName) { this.itemName = itemName; }
    
    @XmlElement
    public int getQuantity() { return quantity; }
    
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
    
    @XmlElement
    public String getCustomerName() { return customerName; }
    
    public void setCustomerName(String customerName) { this.customerName = customerName; }
}
 
class OrderList
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "OrderList")
public class OrderList {

    List orders;
   
    @XmlElement(name = "order")
    public List getOrder() {
        if (orders == null) {
            orders = new ArrayList();
        }
        return this.orders;
    }
}
 
The web.xml file specify only the CXF servlet:
<web-app>

    <display-name>Archetype Created Web Application</display-name>
   
 <servlet>
  <servlet-name>CXFServlet</servlet-name>
  <servlet-class>
   org.apache.cxf.transport.servlet.CXFServlet
  </servlet-class>
   <load-on-startup>1</load-on-startup> 
 </servlet>

 <servlet-mapping>
  <servlet-name>CXFServlet</servlet-name>
  <url-pattern>/api/*</url-pattern>
 </servlet-mapping>
 
</web-app> 
 
The file cxf-servlet.xml specify the service bean and the JAX-RS providers:
 <beans 
    xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:beans='http://www.springframework.org/schema/beans'
 xmlns:jaxrs="http://cxf.apache.org/jaxrs"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://cxf.apache.org/jaxrs 
 http://cxf.apache.org/schemas/jaxrs.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd">
  

  <jaxrs:server id="restContainer" address="/" >
  
            <jaxrs:serviceBeans>
        <ref bean="simpleService" />
     </jaxrs:serviceBeans>  
     
           <jaxrs:providers>
               <ref bean="jaxbProvider"/>
               <ref bean="jsonProvider"/> 
           </jaxrs:providers>
      
        </jaxrs:server>
     
     <bean id="simpleService" class="com.acme.poc.restapi.cxf.service.SimpleServiceImpl" />
  <bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider"/>  
  <bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
  
</beans> 
 
The SimpleService interface: 
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;  

@Path("/order/")
public interface SimpleService {
 @GET
 @Produces("application/xml")
 @Path("{orderId}")
 public Order getOrderXml(@PathParam ("orderId") int id);
 
 @GET
 @Produces("application/json") 
 @Path("/")
 public Order getOrderJson(@QueryParam("id") @DefaultValue("-1") String strId);
 
 @GET
 @Produces("application/xml")
 @Path("all")
 public OrderList getAllOrders();
} 
 
The SimpleServiceImpl class: 
import java.util.ArrayList;
import java.util.List;

public class SimpleServiceImpl implements SimpleService {
 
 List list = new ArrayList();
 
 public SimpleServiceImpl () {
         Order order = new Order();
         order.setItemName("Veggie Pizza");
         order.setQuantity(9);
         order.setCustomerName("OptumInsight");
         list.add(order);
         
         order = new Order();
         order.setItemName("Green Salad");
         order.setQuantity(2);
         order.setCustomerName("OptumInsight");
         list.add(order);
  }
 
 @Override
    public Order getOrderXml(int id) {
   return getOrder(id);
    }
 
 @Override
 public Order getOrderJson(String strId) {
  int id = Integer.valueOf(strId);
  return getOrder(id);
 }
 
 @Override
 public OrderList getAllOrders() {
  OrderList fullList = new OrderList();
        for(Order order : list) {
         fullList.getOrder().add(order);
        }
        return fullList;
 }
 
 // Common method returning an Order POJO
 public Order getOrder(int id) {
  if ((id > 0) && (id <= list.size())) {
   return list.get(id - 1);
  }
         else
          return null;
 }
}
 
Finally the pom.xml file for maven: 
<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.acme.poc.restapi</groupId>
  <artifactId>poc_restapi_cxf_client</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>

  <name>poc_restapi_cxf_client</name>
  <url>http://maven.apache.org</url>
  
    <properties>
      <spring.version>3.1.0.RELEASE</spring.version>
      <cxf.version>2.6.0</cxf.version>
      <jackson.version>1.9.7</jackson.version>
      <junit.version>4.8.1</junit.version>    
   </properties>
  
   <dependencies>
   
     <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <version>${cxf.version}</version>
   <scope>provided</scope>
     </dependency>
   
     <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-extension-search</artifactId>
      <version>${cxf.version}</version>
    <scope>provided</scope>
     </dependency>
     
     <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-extension-providers</artifactId>
      <version>${cxf.version}</version>
    <scope>provided</scope>
     </dependency>
  
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    
  </dependencies>
  
  <build>
    <finalName>poc_restapi_cxf_client</finalName>
     <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>UTF-8</encoding>
            </configuration>
         </plugin>
    </plugins>
  </build>
</project>



The file jboss-deployment-structure.xml is used for JBOSS deployment: 
<jboss-deployment-structure>
 <deployment>
  <dependencies>
   <module name="org.apache.cxf" services="import">
    <imports>
     <include path="**" />
    </imports>
   </module>
  </dependencies>
 </deployment>
</jboss-deployment-structure>
 
 
In addition to this, before you deploy on JBOSS, you need to make some modifications on your web application server:

1) Download the latest edition of CXF (2.6.0) and placed just one jar from the distribution into the folder /modules/org/apache/cxf/main/cxf-2.6.0.jar (This contains all of the CXF codebase, except the dependencies)
  
2) Open /modules/org/apache/cxf/main/module.xml
   Remove all resource elements (the JARs)
   Add a new resource element similar to the deleted ones with cxf-2.6.0.jar being the only Jar referenced
   Add dependency to module named: javax.ws.rs.api :


<module xmlns="urn:jboss:module:1.1" name="org.apache.cxf">
   <resources>
        <resource-root path="cxf-2.6.0.jar"/>
        <!-- 
        <resource-root path="cxf-api-2.4.4.jar"/>
          ...
        <resource-root path="cxf-xjc-ts-2.4.0.jar"/> 
        -->
    </resources>
     <dependencies>
        <module name="asm.asm" />
        <module name="javax.api" />
        <module name="javax.annotation.api" />
        <module name="javax.jms.api" />
        <module name="javax.jws.api" />
        <module name="javax.mail.api" />
        <module name="javax.resource.api" />
        <module name="javax.servlet.api" />
        <module name="javax.ws.rs.api" />
        <module name="javax.xml.bind.api" services="import"/>
        <module name="com.sun.xml.bind" services="import"/>
        <module name="javax.wsdl4j.api" />
        <module name="javax.xml.soap.api" />
        <module name="javax.xml.stream.api" />
        <module name="javax.xml.ws.api" />
        <module name="org.apache.commons.lang" />
        <module name="org.apache.neethi" />
        <module name="org.apache.velocity" />
        <module name="org.apache.xml-resolver" />
        <module name="org.apache.ws.xmlschema" />
        <module name="org.apache.ws.security" />
        <module name="org.apache.santuario.xmlsec" />
        <module name="org.springframework.spring" optional="true"/>
    </dependencies>
</module> 
 
3) Create folder /modules/org/springframework/spring/main/
   Create module.xml
   Copy all the text from the CXF module.xml into this new one
   Erase the last dependency (itself)
   Erase the resource against cxf JAR  (because this is the copy for spring)
   Download latest Spring framework, and copy all the Spring jars into the folder
   Add all of these JARs to the module.xml inside tag.
   Add dependency to module named: org.apache.commons.logging 


<module xmlns="urn:jboss:module:1.1" name="org.springframework.spring">
    <resources>
  <resource-root path="spring-aop-3.1.0.RELEASE.jar" />
  <resource-root path="spring-asm-3.1.0.RELEASE.jar" />
  <resource-root path="spring-beans-3.1.0.RELEASE.jar" />
  <resource-root path="spring-context-3.1.0.RELEASE.jar" />
  <resource-root path="spring-core-3.1.0.RELEASE.jar" />
  <resource-root path="spring-expression-3.1.0.RELEASE.jar" />
  <resource-root path="spring-web-3.1.0.RELEASE.jar" />
        <!-- Insert resources here -->
    </resources>
    <dependencies>
        <module name="asm.asm" />
        <module name="javax.api" />
        <module name="javax.annotation.api" />
        <module name="javax.jms.api" />
        <module name="javax.jws.api" />
        <module name="javax.mail.api" />
        <module name="javax.resource.api" />
        <module name="javax.servlet.api" />
        <module name="javax.xml.bind.api" services="import"/>
        <module name="com.sun.xml.bind" services="import"/>
        <module name="javax.wsdl4j.api" />
        <module name="javax.xml.soap.api" />
        <module name="javax.xml.stream.api" />
        <module name="javax.xml.ws.api" />
        <module name="org.apache.commons.lang" />
        <module name="org.apache.commons.logging" />
        <module name="org.apache.neethi" />
        <module name="org.apache.velocity" />
        <module name="org.apache.xml-resolver" />
        <module name="org.apache.ws.xmlschema" />
        <module name="org.apache.ws.security" />
        <module name="org.apache.santuario.xmlsec" />
    </dependencies>
</module>

Thursday, April 19, 2012

java XSLT processing with Saxon



Recently, I was looking for an efficient way to reduce the size of XML based clinical documents (CCD) that I am sending from a java REST API to mobile devices. These healthcare HL7 XML documents contain textual narrative blocks that are not used by the applications residing on the mobile devices.

On solution was to use Saxon, an open source XSLT processor in conjunction with a transformation style sheet to strip the Continuity of Care Documents of their narrative blocks.

In a CCD, each section under ClinicalDocument/component/structuredBody/component can have a text section that usually contains a narrative block similar to HTML content that can typically add 30% or more to the size of the CCD document:










































This has the advantage to avoid creating a full DOM representation, and is more robust and flexible than an event based sequential SAX processor.

The first step is to create a XSLT stylesheet that will transform (in my case strip the text sections) from the XML document.
In my case, since I wanted to remove all text sections, I was able to use an identity transform mechanism that I modified to strip the text sections (uses the hl7 namespace):

<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='2.0'
xmlns:hl7="urn:hl7-org:v3">

<xsl:output indent="no"/>
<xsl:strip-space elements="*"/>

<!-- identity template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>

<!--  remove descriptive sections -->
<xsl:template match="hl7:text"/>  

</xsl:stylesheet>


Then I placed this file (I named it ccd_no_text_sections.xsl)  under the /src/main/resources folder.

To use this XSLT stylesheet with Saxon, you need to use one of the Saxon Java libaries.
If you using maven, for example, just add the dependencies to your pom file based on the version you want to use:
<dependency>
 <groupId>net.sf.saxon</groupId>
 <artifactId>saxon</artifactId>
 <version>9.0</version>
</dependency>

<dependency>
 <groupId>net.sf.saxon</groupId>
 <artifactId>saxon-dom</artifactId>
 <version>9.0</version>
</dependency>

or for the latest free version:

<dependency>
 <groupId>net.sf.saxon</groupId>
 <artifactId>Saxon-HE</artifactId>
 <version>9.4</version>
</dependency>

Then you need to create a transformer and call your style sheet for the transformation.
In my case, since I was planning other transformations, I decided to cash the XSLT transformer into a HashTable as follow:

import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class XSLTProcessor {

 private static final HashMap<String,Transformer> TRANSFORMER_CACHE = new HashMap<String,Transformer>();

 final static String CCD_NO_TEXT_SECTIONS = "/ccd_no_text_sections.xsl";
  
 private XSLTProcessor() {
   // no instantiation
 }

 private static Transformer getCCDTransformer() throws TransformerConfigurationException {

  Transformer transformer = TRANSFORMER_CACHE.get(CCD_NO_TEXT_SECTIONS);
  if (transformer == null) { 
   TransformerFactory tsf = TransformerFactory.newInstance();
         InputStream is = XSLTProcessor.class.getResourceAsStream(CCD_NO_TEXT_SECTIONS);
   transformer = tsf.newTransformer(new StreamSource(is));
   return transformer;
  }
  return transformer;
 }
 
 public static String stripTextSections(final String xmlString) throws TransformerConfigurationException,
        TransformerException, 
        TransformerFactoryConfigurationError {

        final StringReader xmlReader = new StringReader(xmlString);
        final StringWriter xmlWriter = new StringWriter();
        final Transformer ccdTransformer = getCCDTransformer();
        ccdTransformer.transform(new StreamSource(xmlReader),
            new StreamResult(xmlWriter));
        
        return xmlWriter.toString();
 }
}


In this way, you can easily add new transformers if needed and refer to them by their stylesheet name located under the resource folder.
In addition to this, the transformer is created only once and always available for a transformation.

The transformation can be called directly on the document as follow:

try {
    String new_ccd = XSLTProcessor.stripTextSections(ccd);
}
catch (TransformerConfigurationException e) {
    log.error("TransformerConfigurationException"+e);
}
catch (TransformerException e) {
    log.error("TransformerException"+e);
}
catch (TransformerFactoryConfigurationError e) {
    log.error("TransformerFactoryConfigurationError"+e);
}

Friday, March 23, 2012

X-HTTP header method override and REST APIs





In a previous article, I was explaining how to extract HTTP headers. In this one, I will explain why the use of specific custom HTTP headers such as X-HTTP methods override can be very handy while developing and promoting a REST API.

When deploying REST API based web services, you may encounter access limitations on both the server and client sides.







1) X-HTTP methods override and server side

On the server side, the access to your REST based  web application (I am using JBOSS) might be done via a proxy web server that does not support certain HTTP operations such as DELETE or PUT for security reasons.

For example if you are using Lotus Domino as redirector for your HTTP requests, be aware that by default Lotus Domino does not enable PUT and DELETE to go through.

Such requests will lead to HTTP 405 error:

Http Status Code: 405

Reason: Request method is not allowed by the server

By the way, if you really intend to allow PUT and DELETE on your Domino proxy web server, you need to add the following line to your notes.ini file:

HTTPEnableMethods=PUT,DELETE

However if you really need to have your proxy server filtering DELETE and PUT operations, then you will have to offer these operations via POST operations and ask your clients to add specific X-HTTP method override field in the header of their requests.



2) X-HTTP methods override and client side

The application consuming your web service might also have some constraints, for example restricting forms to only use GET or POST.

For these reasons, web infrastructure and solutions providers have proposed to use customized HTTP header fields:
If you want to support all of these variations, I would suggest to implement them as case insensitive in your REST API.

I personally use JBoss RestEasy to develop a REST API for mobile health care applications, and it was quite straightforward to add the additional level of flexibility to my existing PUT and POST operations.

Note that if you want to use these methods to enforce either a PUT or a DELETE method, be aware that the RFC 2616 - HTTP 1.1 section 9.1.1 describes the GET operation as safe.

9.1.1 Safe Methods

   Implementors should be aware that the software represents the user in
   their interactions over the Internet, and should be careful to allow
   the user to be aware of any actions they might take which may have an
   unexpected significance to themselves or others.

   In particular, the convention has been established that the GET and
   HEAD methods SHOULD NOT have the significance of taking an action
   other than retrieval. These methods ought to be considered "safe".
   This allows user agents to represent other methods, such as POST, PUT
   and DELETE, in a special way, so that the user is made aware of the
   fact that a possibly unsafe action is being requested.


In other words, if you are tempted to use a GET to simulate a PUT or DELETE, don't do it.
Use a POST instead. Thank you to my colleague Ravi to point this to me!


X-HTTP methods override and PUT based operations

If your PUT operation does not have a corresponding DELETE counter-part,
you could just use the POST as a PUT and not even add a X-HHTP-Method override.

Both POST and PUT requests will end up being bound to the same method:


 @PUT
 @POST
 @Path("/users/{userId}/order/{orderNum}")
 @Produces("application/json")
 @GZIP
 public Response putPostUserOrder(@Context HttpServletRequest request, 
               @PathParam("userId") String userId,
               @PathParam("orderNum") String orderNum)

If you still need to use a X-HTTP-Method override, what you need to do is to filter those specific POST requests.

boolean bProceed = true;
if (request.getMethod().compareToIgnoreCase(POST) == 0) {
    String methodOverride = getHttpMethodOverride(request);
    if ((methodOverride == null) || (methodOverride.compareToIgnoreCase(PUT) != 0)) 
        bProceed = false;
}

private String getHttpMethodOverride(HttpServletRequest request){
    String headerValue = request.getHeader("x-http-method-override");
    if (headerValue != null)
      return headerValue;
    else 
      return(request.getHeader("x-method-override"));  
}

In the example above, I have only implemented two customized X-HTTP headers, but you can add all of them if necessary.



X-HTTP methods override and DELETE based operations

For the DELETE operation, even if you don't have a PUT counter part, it is preferable to use a X-HTTP method override with DELETE to be semantically consistent (a PUT and POST are typically using to add or modify a resource, not removing it).

 
 @POST
 @Path("/users/{userId}/order/{orderNum}")
 @Produces("application/json")
 @GZIP
 public Response postDeleteUserOrder(@Context HttpServletRequest request, 
               @PathParam("userId") String userId,
               @PathParam("orderNum") String orderNum)

boolean bDelete = false;
if (request.getMethod().compareToIgnoreCase(POST) == 0) {
 String methodOverride = getHttpMethodOverride(request);
 if ((methodOverride != null) && (methodOverride.compareToIgnoreCase(DELETE) == 0)) 
  bDelete = true;
}

Of course, if most likely want to still support the regular DELETE, so you also keep the original binding DELETE method:

 @DELETE
 @Path("/users/{userId}/order/{orderNum}")
 @Produces("application/json")
 @GZIP
 public Response deleteUserOrder(@Context HttpServletRequest request, 
               @PathParam("userId") String userId,
               @PathParam("orderNum") String orderNum) 
 
 

Test harnessing X-HTTP Methods override operations


You can easily test your REST API with cURL as follow:

curl -H "X-HTTP-Method-Override: DELETE" -X POST http://restservice.acme.com/api/users/JJALGHPBIMA/orderNum/e7687673-479d-4d37-8bc0-a3e718aad33f

Should have the same effect as :

curl -X DELETE http://restservice.acme.com/api/users/JJALGHPBIMA/orderNum/e7687673-479d-4d37-8bc0-a3e718aad33f




Friday, December 30, 2011

How to extract HTTP header fields in a REST API



To be able to extract HTTP headers fields from incoming requests accessing a REST API is particularly useful when trying to log the transactions that are coming through the REST based service.

In this example, I use Java based JBoss RESTEasy together with @Context java annotation to extract the header information.

One of the REST API operation is /version that returns the current version of the API in a JSON format.The interface operation is defined as follow:

     import javax.ws.rs.core.HttpHeaders;

     /**
     * Get the current version of the REST API.
     */
     @GET
     @Path("/version")
     @Produces("application/json")
     @GZIP
     public Response getVersion(@Context HttpHeaders headers);

The @Context annotation allows you to map request HTTP headers to the method invocation.

One way to safely extract all available header parameters is to loop through the headers.The method getRequestHeaders from javax.ws.rs.core.HttpHeaders returns the values of HTTP request headers. The returned map is case-insensitive and is read-only.

   if (headers != null) {
       for (String header : headers.getRequestHeaders().keySet()) {
          System.out.println("Header:"+header+
                             "Value:"+headers.getRequestHeader(header));
       }
   }

When querying the REST API via a browser:

  http://www.acme.com/api/version

you obtain a minimum set of headers fields:

   Header:cache-control Value:[max-age=0]
   Header:connection Value:[keep-alive]
   Header:accept-language Value:[en-us,en;q=0.5]
   Header:host Value:[www.acme.com]
   Header:accept Value:[text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8]
   Header:user-agent Value:[Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0)    
   Gecko/20100101 Firefox/8.0]
   Header:accept-encoding Value:[gzip, deflate]
   Header:session-id Value:[d636369c]
   Header:accept-charset Value:[ISO-8859-1,utf-8;q=0.7,*;q=0.7]

To be able to extract a specific header field (e.g. Host), you can use the getRequestHeader function:

 public Response getVersion(@Context HttpHeaders headers) {
     if (headers != null) {
        List<String> hostHeader = headers.getRequestHeader("host");
        if (hostHeader != null) {
           for (String host : hostHeader) {
              LOG.debug("Host:"+host);
           }
        }
     } 
  
     // Get the version
     final Version current_version = new Version();
     final ObjectMapper mapper = new ObjectMapper();
     final JsonNode rootNode = mapper.createObjectNode();
     ((ObjectNode) rootNode).putPOJO(Version.XML_ROOT_ELEMENT, current_version);
     
     final ResponseBuilder builder = Response.ok(rootNode);
     return builder.build();
   }

If you are using a test harness tools like cURL, you can also easily add additional HTTP header fields
(e.g. the email address of the user making the request or the referer) and test the logging functionality of your REST API:
curl -H "From: user@example.com" http://www.acme.com/api/version
curl -H "Referer: http://consumer.service.acme.com" http://www.acme.com/api/version

The headers can also be obtained through the HttpServletRequest object.
This can be done using the @Context HttpServletRequest annotation and can provide additional information about the incoming request:

public Response getVersion(@Context HttpServletRequest request) {
   LOG.debug("Host:"+request.getHeader("host"));
   LOG.debug("Request-URL:"+request.getRequestURL());
   ...
} 

Since HttpServletRequest extends ServletRequest you also getting useful methods providing information on the Internet Protocol (IP) address, host and port of the client or last proxy that initiated the request.

public Response getVersion(@Context HttpServletRequest request) {
   LOG.debug("Remote-IP:"+request.getRemoteAddr());
   LOG.debug("Remote-Host:"+request.getRemoteHost());
   LOG.debug("Remote-Port:"+request.getRemotePort());
   ...
} 
 

Friday, September 23, 2011

cURL tests harness and TLS



In a previous post, I have explained how to use cURL to test harness REST based Web services. One thing I did not described was how to add Transport Layer Security (TLS) in your tests. In other words, how to successfully test REST Web services over HTTPS?

The cURL manual describes a certain number of options that can be used. One of the most convenient option is -k or --insecure. It allows curl to  perform  "insecure" SSL connections and transfers. All SSL connections are attempted to be made secure by using the CA certificate  bundle  installed by  default. So if you SSL connection does not require client side authentication it is a very quick way to test your web service over SSL:

  curl -k https://www.acme.com/api/version


Another useful option can be used if in conjunction to SSL, you need to compress your payload via GZIP for example to optimize the transfer of large messages. In this case, you will use the option -H or --header that will help you specify custom headers to your request:

  curl -H "Accept-Encoding: gzip,deflate" -k https://www.acme.com/api/users/list


Notice in that case that the -k or --insecure option is always placed just in front of URL.

Of course, other headers such as the one described previously can be combined:

  curl -c ./cookies.txt --data-binary @login_password.json -H "Content-Type: application/json" -H "Accept-Encoding: gzip,deflate" -k https://www.acme.com/api/users/token







Thursday, April 28, 2011

How to test harness REST web services with cURL




cURL is a very convenient command line tool to send and retrieve data using the URL syntax. It supports a large number of protocols: HTTP, HTTPS, FTP, FTPS, SCP, SFTP, TFTP, LDAP, LDAPS, DICT, TELNET, FILE . IMAP, POP3, SMTP and RTSP.

I have been using cURL for quickly and conveniently testing RESTFul APIs.

Since I am doing most of my development on Windows platforms, I am using cURL in conjunction with Console, a Windows console window enhancement tool.

 First you need to download and install cURL for your platform (windows, Linux ect). Then you can do a quick test by accessible the home page of your favorite web site:









This should display the HTML content of the home page. At this point I would suggest to look at the documentation to see what you can do with cURL including the FAQ.

I usually use simple test harness such as 'curl http://www.acme.com/api/versions' if you REST API exposes available versions.

You can then start to do more sophisticated tests such as authentication credentials from files (in my case I use a JSON data structure to automatically authenticate to my web service), saving cookies on files (using the the -c option):

curl -c ./cookies.txt --data-binary @login_password.json -H "Content-Type: application/json"  http://www.acme.com/api/users/token

With my JSON file as follow: {"login":{"username":"user1","password":"StrongPassword1"}} 

The cookie in my case contains a session token that I can reuse between each cURL calls. In the next call I read the cookie (via the -b option):

curl -b ./cookies.txt http://www.acme.com/api/users/userid1234567890/orders

The output of the command can be saved in a file using the option -o or redirecting our output:

curl -o google.html www.google.com

or

curl www.google.com > google.html 

Enjoy!

In my next post on this topic, I will explain how to use SSL/TLS and specify GZIP headers to your cURL requests.



Wednesday, June 9, 2010

Open APIs: State of the Market, May 2010

Today, I was looking at the presentation from John Musser related to Open APIs (see below). Even though these statistics comes mainly from mashup and consumer applications, I was surprised by the fact that REST APIs are gaining market shares over SOAP APIs so rapidly.

In B2B and in the enterprise world in general SOAP is often the top choice. The advantages for SOAP often mentioned are:
  • Type checking (via the WSDL files)
  • Availability of development tools
On the other hand, REST offers the following:
  • Lightweight and easy to build
  • Human Readable Results
  • Extensibility
  • Scalability

In Health Care, SOAP is still widespread and prevalent. However there are some interesting projects such as NHIN Direct Health Information Exchange where the relevance of REST vs other API protocols are discussed.

It will be interesting to see what will be the outcome of such discussions.

Tuesday, May 18, 2010

Enhanced POM for JBoss RESTEasy Twitter API Client Sample

I recently looked at JBOSS RESTEasy as a way to create and test RESTful APIs. The platform looks very promising with a lot of praise from developers. Also the documentation seems very extensive and precise.

I started by downloading RESTEasy 1.2.1 GA and tried the sample code. I started with a java client to access existing RESTful Web Services and APIs. Among the api-clients, there is a Twitter small client that works out-of-the box (located under /RESTEASY_1_2_1_GA/examples/api-clients/src/main/java/org/jboss/resteasy/examples/twitter).

However when I started to extract the code and wanted to create a Maven 2 based stand-alone project, I encountered some issues related to JAR dependency conflicts, including the following error message also described here.

java.lang.NoClassDefFoundError: Could not initialize class com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl 

The project (eclipse) structure looks as below:


















I managed to fix these issues by modifying the POM file as follow:

<?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>api-clients</artifactId>
 <version>1.2.1.GA</version>
  
  <dependencies>
    <!-- Resteasy Core -->
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxrs</artifactId>
    </dependency>
    <!-- JAXB support -->
   <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxb-provider</artifactId>
   </dependency>
    
  </dependencies>
  <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-bom</artifactId>
                <version>1.2.1.GA</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
   </dependencyManagement>
   
   <!-- Build Settings --> 
   <build>
    <plugins>  
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
   </build>
  
  <!-- Environment Settings -->
  <repositories>
    <repository>
      <id>jboss</id>
      <name>jboss repo</name>
      <url>http://repository.jboss.org/maven2</url>
     </repository>
   </repositories>
  
</project>

The most important piece, beside the cleaning of the POM file, was to include a pom that can be imported so the versions of the individual modules do not have to be specified (see RESTEasy documentation - Chapter 43. Maven and RESTEasy).

I also made sure to have correct dependencies for resteasy-jaxrs and resteasy-jaxb-provider.

As a result, I was able to compile the whole project without any errors (mvn clean compile) and run it to access the Twitter REST API

mvn exec:java -Dexec.mainClass="org.jboss.resteasy.examples.twitter.TwitterClient" -Dexec.args="<userid> <password>"
(Replace last parameters by your twitter user and password).

The small client in question leverages JAX-RS annotations to read and write the Twitter API resources:

package org.jboss.resteasy.examples.twitter;

import java.util.Date;
import java.util.List;

import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.client.ClientExecutor;
import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;

public class TwitterClient
{
   static final String friendTimeline = "http://twitter.com/statuses/friends_timeline.xml";

   public static void main(String[] args) throws Exception
   {
      RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
      final ClientExecutor clientExecutor = new ApacheHttpClientExecutor(createClient(args[0], args[1]));
      TwitterResource twitter = ProxyFactory.create(TwitterResource.class,
            "http://twitter.com", clientExecutor);
      System.out.println("===> first run");
      printStatuses(twitter.getFriendsTimelines());
      
      twitter
      .updateStatus("I programmatically tweeted with the RESTEasy Client at "
            + new Date());
      
      System.out.println("===> second run");
      printStatuses(twitter.getFriendsTimelines());
   }

   public static interface TwitterResource
   {
      @Path("/statuses/friends_timeline.xml")
      @GET
      Statuses getFriendsTimelines();

      @Path("/statuses/update.xml")
      @POST
      Status updateStatus(@FormParam("status") String status);
   }

   private static void printStatuses(Statuses statuses)
   {
      for (Status status : statuses.status)
         System.out.println(status);
   }

   private static HttpClient createClient(String userId, String password)
   {
      Credentials credentials = new UsernamePasswordCredentials(userId,
            password);
      HttpClient httpClient = new HttpClient();
      httpClient.getState().setCredentials(AuthScope.ANY, credentials);
      httpClient.getParams().setAuthenticationPreemptive(true);
      return httpClient;
   }

   @XmlRootElement
   public static class Statuses
   {
      public List<Status> status;
   }

   @XmlRootElement
   public static class Status
   {
      public String text;
      public User user;

      @XmlElement(name = "created_at")
      @XmlJavaTypeAdapter(value = DateAdapter.class)
      public Date created;

      public String toString()
      {
         return String.format("== %s: %s (%s)", user.name, text, created);
      }
   }

   public static class User
   {
      public String name;
   }

}


The small DateAdapter class is a utility class for date formatting:

package org.jboss.resteasy.examples.twitter;

import java.util.Date;
import java.util.List;
package org.jboss.resteasy.examples.twitter;
 
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.jboss.resteasy.util.DateUtil;

public class DateAdapter extends XmlAdapter<String, Date> {

   @Override
   public String marshal(Date date) throws Exception {
       return DateUtil.formatDate(date, "EEE MMM dd HH:mm:ss Z yyyy");
   }

   @Override
   public Date unmarshal(String string) throws Exception {
       try {
           return DateUtil.parseDate(string);
       } catch (IllegalArgumentException e) {
           System.err.println(String.format(
                   "Could not parse date string '%s'", string));
           return null;
       }
   }
}

Friday, April 16, 2010

SOA and Health Care Meaningful Use requirements of the Recovery Act


The Interim Final Rule of the Health Information Technology for Economic and Clinical Health (HITECH) Act was passed by Congress in February of 2009.  Under this act, eligible providers will be given financial rewards if they demonstrate "meaningful use" of "certified" Electronic Health Record (EHR) technologies.

Therefore there is a big incentive for health care vendors to offer solutions that meet the criteria described in the law.  More precisely, the associated regulation provided by the Department of Health and Human Services describes the set of standards,  implementation, specifications and certification for Electronic Health Record (EHR) technology.


As a Software Architect, I was curious to see whether Service Oriented Architecture (SOA) or Web Services in general were mentioned in these documents.

The definition of an EHR Module includes an open list of services such as electronic health information exchange, clinical decision support, public health and health authorities information queries, quality measure reporting etc.

In the transport standards section, both SOAP and RESTful Web services protocols are described. However Service Oriented Architecture (SOA) is never explicitly described or cited. No reference how these services might be discovered and orchestrated in a "meaningful way". I would assume that the reason is that the law makers and regulators wanted to be as vague as possible on the underlying technologies for an EHR and its components.

The technical aspect of "meaningful use" is specified more precisely when associated with interoperability, functionality, utility, data confidentiality and integrity of the data, security of the health information system in general.

These characteristics are not necessarily specific to SOA, but to any good health care software and solution design.

Still, the following paragraph seems to describe a solution that could be best implemented using a Service Oriented Architecture: "As another example, a subscription to an application service provider (ASP) for electronic prescribing could be an EHR Module"  where software is offered as a service (SaaS).  This looks more like the description of an emerging SOA rather than a full grid enabled SOA.

It will be up to the solutions providers to come up with relevant products and tools to maximize the return on investment (ROI) of the tax payer's money and the professionals and organizations eligible for ARRA/HITECH.

SOA will definitively be part of the mix since it gives the ability create, offer and maintain large numbers of complex EHR Software solutions (SaaS) that have a high level of modularization and interoperability.
 
Further developments toward a complete SOA stack such as offering a Platform as a Service (PaaS) and even the underlying Infrastructure as a Service (IaaS) in the cloud will face more resistance in a domain known for a lot of legacy systems and concerns about privacy and security.

The Object Management Group (OMG) is organizing a conference this summer on the topic of  "SOA in Healthcare: Improving Health through Technology: The role of SOA on the path to meaningful use". It will be interesting to see what healthcare providers, payers, public health organizations and solution providers from both the public and private sector will have to say on this topic.