Thursday Nov 12, 2009

JTF: Running tests against an app deployed on a staging server

          Few days back I saw somebody asking whether the Jersey Test Framework allows you to run your tests on an application which is deployed on a staging server. The answer was NO at that point of time. Then I realized that this is a common use case and that it would be good to add this support.

         With this release of Jersey 1.1.4, the Jersey Test Framework now lets you run your tests against your application deployed on a staging server. All that you have to do is just set this property JERSEY_HOST_NAME to the IP Address or the domain name of the machine i.e., the staging server, on which your application is pre-deployed.

        Lets say you have your application deployed on a machine with IP 129.132.212.54, tests could be run using the following command on your local machine:

 mvn clean test -Dtest.containerFactory=com.sun.jersey.test.framework.spi.container.external.ExternalTestContainerFactory  -DJERSEY_HOST_NAME=129.132.212.54 -DJERSEY_HTTP_PORT=<http_port>


  •  The support for specifying the JERSEY_HOST_NAME is limited only to the external container types.
  • Apart from the JERSEY_HOST_NAME you would also need to set the JERSEY_HTTP_PORT to the HTTP port on which your server is listening.
  • For more information on the various container types supported by the framework, please refer the previous blog entry on the framework.



Tuesday Aug 25, 2009

Jersey Test Framework re-visited!

   One of the previous entries introduced the Jersey Test Framework, which has since been adopted and used by a good number of developers. However, there has been some feedback suggesting ways for making the framework a better one.

Based on all this feedback, we have worked on making some changes in the framework. With the release of Jersey 1.1.2-ea, we have this new version of the framework which is better than the previous version in the following ways:

  • Introduced the concept of test container factories
  • Various test container types, defined by the different test container factory implementations
  • Support for the new In-Memory or In-Process test container
  • Loosely-coupled with the test container factory implementations
  • Loose coupling allows the definition and pluggability of custom test container factory implementations
  • Support for running tests on an application pre-deployed on an external container

But, there have been some major changes in the API, which seemed obvious for the cause.

This entry will describe what are the API changes, and how an user test can be defined, etc.

Breaking changes from 1.1.1-ea to 1.1.2-ea

  • The maven project groupId has changed from “com.sun.jersey.test.framework” to “com.sun.jersey”. 
                <dependency>
                    <groupId>com.sun.jersey</groupId>
                    <artifactId>jersey-test-framework</artifactId> 
                    <version>1.1.2-ea</version>
                <dependency>
  • The extending of Jersey unit test and configuration has changed.

                   The test class has to just pass an instance of AppDescriptor. For instance, the constructor of the spring-annotations sample test, passes this information as follows:

 public SpringAnnotationsWebAppTest() throws Exception {
      super(new WebAppDescriptor.Builder("com.sun.jersey.samples.springannotations.resources.jerseymanaged")
                .contextPath("spring")
                .contextParam("contextConfigLocation", "classpath:applicationContext.xml")
                .servletClass(SpringServlet.class)
                .contextListenerClass(ContextLoaderListener.class)
                .build());
    }

           Note the use of the Builder design pattern, which makes it really easy to define an instance of the AppDescriptor while defining all the application attributes.

  • The test container type with which to run the tests has to be specified using the System Property test.containerFactory. Note that it used to be container.type till the previous version.
  • Unlike the previous implementation, the test container type value is not a string which maps to the container type, but the fully qualified class name of the test container factory is passed as value for the property test.containerFactory.
  •    mvn test -Dtest.containerFactory=com.sun.jersey.test.framework.spi.container.grizzly.web.GrizzlyWebTestContainerFactory 
    

About the AppDescriptor

            AppDescriptor is an abstract class which is extended by two classes - the LowLevelAppDescriptor and the WebAppDescriptor. These classes allow the definition of the various attributes of the application - like its context-path, url-pattern, root resource classes or packages, etc. While the LowLevelAppDescriptor can be used is cases were tests are to be run on light-weight containers like Grizzly or HTTPServer, the WebAppDescriptor is used in cases where tests could be run on the web-based containers like EmbeddedGlassFish, Grizzly Web Container, and the light-weight containers as well\*.

Test Container Factories

       The test framework comes with a set of test container factory implementations which are responsible for creating the test container(s).

The following low-level test container factories are provided:

The following Web-based test container factories are provided:

Running Tests using Maven

      As previously said, the container on which the tests have to be run is specified using the system property test.containerFactory which holds the fully-qualified classname of the test container factory which creates an instance of the test container, i.e.,

 
  
          mvn clean test -Dtest.containerFactory=<container-factory fully-qualified class name>

      Note:

           1. If tests are to be run on external container like GlassFish, the application has to be explicity deployed on the container before running the tests.

               a. Package the application:

               mvn clean package -Dmaven.test.skip=true

               b. Deploy the generated application war file 

               c. Run the tests:

              mvn test -Dtest.containerFactory=com.sun.jersey.test.framework.spi.container.external.ExternalTestContainerFactory

           2. If the  tests are to be run on EmbeddedGlassFish, one additional property container.type has to be set along with test.containerFactory:

              mvn clean test -Dtest.containerFactory=com.sun.jersey.test.framework.spi.container.embedded.glassfish.EmbeddedGlassFishTestContainerFactory -Dcontainer.type=EmbeddedGF 

           3. If the property test.containerFactory is not set, the tests would be run on the Grizzly Web container by default.

 
  

Enable Logging

The framework allows the logging of the HTTP requests and responses being sent over the wire during the test process. All that is needed to enable this logging is set the flag enableLogging.

           mvn clean test -Dtest.containerFactory=<test container factory class> -DenableLogging

Programmatically setting the test container factory

      The framework also allows setting the test container factory programmatically. This could be done by overriding the JerseyTest class's getTestContainerFactory method and returning the appropriate test container factory's instance. For example if Grizzly Web container has to be set as the default test container factory, it could be done as follows:

    @Override
    protected TestContainerFactory getTestContainerFactory() {
        return new GrizzlyWebTestContainerFactory();
    }

                       That's a brief description of the new version of the Jersey Test Framework. Please send an email to the Jersey user's mailing list users@jersey.dev.java.net  in case you have any issues. Wish you a happy testing of your RESTful Web Services :)       

Thursday Apr 16, 2009

Jersey Test Framework makes it easy!

   Does your application have RESTful Web Services? Do you want to ensure that these services are working properly on a wide range of containers - both light weight and heavy weight ones?

     Have you ever felt the need of an infrastructure setup, which you can use to test your services against all these containers without having to worry about things like deployment descriptors, etc? If so, you have a news. Jersey 1.0.3 got released day before yesterday, and it comes with a testing framework called the Jersey Test Framework

     The Jersey Test Framework currently allows you to run your tests on any of the following three light weight containers:

The framework is built over JUnit 4.x using Maven.

How do I use the Jersey Test Framework?

          Using the framework is simple. All that you will need  is do this:

  1. Add the following dependency to your pom.xml:
    • <dependency>
                  <groupId>com.sun.jersey.test.framework</groupId>
                  <artifactId>jersey-test-framework</artifactId>
                  <version>1.0.3</version>
                  <scope>test</scope>
       </dependency>
  2. Create a class which extends com.sun.jersey.test.framework.JerseyTest.
  3. Some minimal number of parameters need to be passed by the test class to the JerseyTest class. This can be done in one of the following ways:
    • super(String rootResourcePackage): Pass the root resource package name to the super constructor. This constructor will then take care of initialising, starting and/or stopping the test container.
    • super(String contextPath, String servletPath, String resourcePackageName): Pass the application context path, servlet path and root resource package name to the super constructor if you are working on a web application. Again this constructor will take care of initializing, starting and/or stopping the test container.
    • super(): When you call the default no parameter super constructor, you still can pass the information to the JerseyTest class by creating an instance of the com.sun.jersey.test.framework.util.ApplicationDescriptor class, setting the parameters using the setter methods defined in that class. This has to be done in your test class's constructor. Also, a call needs to be made to the JerseyTest class's setupTestEnvironment(ApplicationDescriptor applicationDescriptor) method. This call would take care of the init, start and/stop of the test container.
  4. Annotate your test methods with the org.junit.Test annotation.
  5. The handles to com.sun.jersey.api.client.Client and com.sun.jersey.api.client.WebResource instances - client and webResource get inherited from the JerseyTest class. You can use them in your test methods for building URIs and sending HTTP requests.
  6. Run the tests using the maven command - mvn clean test. This will by default run the tests against the Grizzly Web Server. If you want to run the tests on the container of your choice, set the system property container.type with one of the following values:
    • EmbeddedGF : Makes the tests run against Embedded GlassFish.
    • GrizzlyWeb : Makes the tests run against the Grizzly Web container.
    • HTTPServer : Makes the tests run against the Simple HTTP Server.
  7. The framework also provides an option of seeing the HTTP requests and responses sent over the wire. It could be done by just setting the system property enableLogging, i.e., if you want to see the request and response sent over the wire, while running tests against Embedded GlassFish, execute the following command:
    • mvn clean test -Dcontainer.type=EmbeddedGF -DenableLogging
  8. And that's it. You have got the framework working.

Are there any samples which are using this framework?

                 Some of the samples that come with the Jersey distribution have been modified to use this framework. These are:

You should try running tests of these samples and see how the test framework works. I'm sure you will like it :)

If you see some of these samples do not have a deployment descriptor, but still you are able to run the tests against Embedded GlassFish. This is because the framework generates a deployment descriptor on the fly in such cases.

Future Enhancements

It is being planned to support the following features in the coming versions:

  1. Support for external containers - GlassFish v2 and GlassFish v3
  2. Giving the user an option to specify the containers which his test doesn't support.

If you have any queries or see any issues with the current implementation or feel there should be something more, please send an email to the Jersey user mailing list - users@jersey.dev.java.net

Sunday Mar 01, 2009

Jersey Client API in Action

      In one of the previous entries we saw that Jersey provides a Client API for consuming RESTful Web Services. In this entry, we shall see how to use this API and consume the HelloWorld service.

     Before we go ahead and create the client, lets overwrite the HelloResource class with the following code:

@Path("hello")
public class HelloResource { 

    /\*\* Creates a new instance of HelloResource \*/
    public HelloResource() {

    }
 
    /\*\*
     \* Retrieves representation of an instance of
     \*  mycompany.resources.HelloResource
     \* @return an instance of java.lang.String
     \*/
    @GET
    @Produces("text/plain")
    public String sayHello(@QueryParam ("name") String name) {
        if ( name != null ) {
            return "Hello " + name + "!";
        }
        return "Hello World!";
    }
 
    @GET
    @Path("{name}")
    @Produces("text/plain")
    public String sayHello2(@PathParam ("name") String name) {
        return "Hello " + name + "!";
    } 

}

              Deploy the application, once this is done. As you see, what we have tried to do is to define two GET methods, one which reads the query parameter "name" and greets the user accordingly, while the other one reads "name" from the URI path segment and greets the user.

Create the Client Application

              For the illustration purpose, let us create a simple console application, which uses the Jersey Client API to consume the service. Also, we will download the Jersey jar files and use them.

1. In the NetBeans IDE, create a new Java Application, by doing File > New Project > Java > Java Application.

2. Name the Java Application as HelloWorldClient and click the Finish button.

3. Download the required jar files - jersey-client.jar, jersey-core.jar and jsr311-api.jar,  from the links mentioned in the Jersey dependencies page.

4. Add these jar files to the Project library, through Project Properties > Libraries > Add JAR/Folder.

5. Overwrite the main() method with the following code:

public static void main(String[] args) {
        // Create Client and Handle to web resources
        String BASE_URI = "http://localhost:8080/HelloWorldWebapp/resources";
        Client client = Client.create();
        WebResource webResource = client.resource(BASE_URI);

        // send a GET request with Accept header set to "text/plain"
        String response = webResource.path("hello").accept(MediaType.TEXT_PLAIN).get(String.class);
        System.out.println(response);

        // send GET request with a query parameter value for 'name'
        response = webResource.path("hello").queryParam("name", "Pranabh").get(String.class);
        System.out.println(response);

        // send GET request to /hello without any query param
        response = webResource.path("hello").get(String.class);
        System.out.println(response);

        // send GET request to /hello/{name}
        response = webResource.path("hello").path("Ranjita").accept(MediaType.TEXT_PLAIN).get(String.class);
        System.out.println(response);

        // send a GET request and get the response encapsulate in ClientResponse
        ClientResponse clientResponse = webResource.path("hello").get(ClientResponse.class);
        System.out.println(clientResponse.getEntity(String.class));
 }

6. The following import statements need to be added too:

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import javax.ws.rs.core.MediaType;

7. Run the application and the following output is seen:

Hello World!
Hello Pranabh!
Hello World!
Hello Ranjita!
Hello World!
 

Lets Dig through the Code

      We created an instance of WebResource using the methods in Client class. Then GET requests were sent to the resources by appending the resource identifier to the web resource handle. Also, in some cases the HTTP Accept header was set using the accept() method. However, in some cases we did not use the accept header, since "text/plain" is the default type.

      Observe the way query parameter name is set in the second GET request. It is set with the value Pranabh, and the response for this request is the string Hello Pranabh!. Similarly, in the fourth GET request, we have passed the value Ranjita for the path segment variable name .

    In the last GET request, we have tried to encapsulate the response in an instance of ClientResponse class instead of String.class as in the previous requests. From this ClientResponse instance we got the response as a String entity.

    All we saw was sending the GET requests, similarly we could send POST, PUT and DELETE methods using the methods post( ), put( ) and delete( ) respectively.

Sunday Feb 15, 2009

The Jersey Client API

                One of the distinguishing features of Jersey is its client API. The Jersey client API makes it really easy to consume the RESTful Web Services - send requests and get back response along with all the required entities. There are two variants of the API - one based on HttpUrlConnection and the other based on the Apache HTTP Client. The API docs (for Jersey version 1.0.2, latest as of the day) are available at:

Steps for consuming a service using the client API:

  • Get an instance of Client by either:
    1. Client client = Client.create(); for the HttpURLConnection based client, or
    2. ApacheHttpClient client = ApacheHttpClient.create(); for the ApacheHttpClient.
  • Get a handle to the resources using the resource(..) method as - WebResource webResource = client.resource(base_url_of_the_service); The base_url could be given either as a String or as java.net.URI.
  • Set the relative path to the required resource using the path() method of the WebResource class
    • Something like webResource.path(path1).path(path2) - "path1" and "path2" are the path elements. Say, I have my web service running at the base URI http://myserver/base, and the resource is identified by http://myserver/base/resources/resource1, then we could set the path to resource using the above as - webResource.path("resources").path("resource1")
    • WebResource follows the builder design pattern for appending path elements to the request URI.
  • Methods mapping each of the four HTTP methods GET, POST, PUT and DELETE are provided. These are:
    • GET ==> get()
    • POST ==> post()
    • PUT ==> put()
    • DELETE ==> delete()
  • If you want to send a GET request to a resource, that can be done using this statement:
    • webResource.path(path1).path(path2).get(Class), where Class is the type of the variable in which the response is to be set to. For example, if the response is to be set to a String variable, it could be done by using  -                                                                                                  String reponse = webResource.path(path1).path(path2).get(String.class);
    • In general, the class is set to Response.class and the response is returned as an object of type com.sun.jersey.api.client.ClientResponse.
  • If you want to send a request with the Accept Header set to some mime-type, that can be done using the accept() method of the WebResource class as shown in the following statement:
    • webResource.path(path1).path(path2).accept(mime-type).get(Class)
  • On the other hand if you want to send some data of some mime-type, it could be achieved using the type() method of the WebResource class as shown in the following statement:
    • webResource.path(path1).path(path2).type(mime-type).post(Object);

     This gives a brief introduction to the Client API. In the next entry, we will use this API to send requests to the service(s) created in the previous entries which used PathParam and QueryParam.

Friday Feb 13, 2009

Making the URI dynamic

            "I have a resource which does something. I want the same thing to be done for another use case. The only thing that differs is some part of the identifier. Do I have to create one more resource for this requirement? Just wondering if there were a way where I could make the identifier dynamic and use the same resource for all such use cases?" - these are some of the common questions that most of the people working on REST get into, at one or the other time.

        Is there really a way, where the URI could be made dynamic??? And the answer is YES.

JAX-RS provides a way of making the URI of the resource dynamic. I know, the next question is HOW???

Lets see how this could be done by modifying the same HelloWorldWebApp developed in one of the previous entries:

  • Modify the @Path annotation given above the HelloResource class as follows:

         @Path("hello/{name}")
          public class HelloResource {

  • Overwrite the sayHello method with the following:

           @GET
           @Produces("text/plain")
            public String sayHello(@PathParam ("name") String name) {
                  return "Hello " + name + "!";        
            }

  • Redeploy the application. Enter the URL http://localhost:8080/HelloWorldWebapp/resources/hello/Rama in a web browser, and you will see the response Hello Rama! . Change the Rama in the URL to Raja and you will see Hello Raja! .

So how is it happening?

   We see two new things here:

  • A different way of passing path information to the @Path annotation 
           The @Path annotation allows the inclusion of path elements which could be dynamically mapped at the time of invocation. Such elements are encoded in {}. The page from the JSR-311 spec describes how path matching is done when such path elements are used in a resource's path definition.
  • Another new annotation - @PathParam

             The @PathParam is another annotation from JAX-RS. This takes care of mapping a path identifier to a method parameter.

Accessing query parameters using @QueryParam

In this entry, we will use the HelloWorldWebApp created in the previous entry. We will modify the resource, so that it takes name of a person as a query parameter and greets him/her. If no name is provided, it will give the general Hello World! message.

   And here are the steps:  

  • Overwrite the previously implemented sayHello method with the following:

    @GET
    @Produces("text/plain")
    public String sayHello(@QueryParam("name") String name) {
        if (name != null) {
            // if the query parameter "name" is there
            return "Hello " + name + "!";
        }
        return "Hello World!";
    }       

  • Import the class javax.ws.rs.QueryParam.
  • Redeploy the application and enter http://localhost:8080/HelloWorldWebapp/resources/hello?name=Rama in a browser. You will see that response Hello Rama!. If the query parameter name is not specified the response would be Hello World!.

So how does this work?

          Here we are using a new annotation @QueryParam. It is an annotation provided by the JAX-RS API. This annotation takes care of mapping a query parameter in a request to a method parameter.

           In this example, we have used this annotation to map the query parameter name to the method parameter name. When a request is sent for the resource with some query parameter value for name, it gets set to the method parameter. For this to happen, the query parameter needs to be mapped to the method parameter. This is done by prefixing the method parameter declaration with @QueryParam and specifying the query parameter name as a string parameter for the annotation. In our example, it is achieved by doing this:

public String sayHello(@QueryParam("name") String name) {

Creating Hello World Service using Jersey

   This blog entry shows how easy it is to build a Hello World RESTful Web Service using Jersey

Required Setup:

  1. Download latest version of NetBeans, if you do not already have it. NetBeans provides wizards for creating RESTful Web Service using Jersey.
  2. If you do not have GlassFish installed in your machine, install one which comes with NetBeans setup. We will use GlassFish for deployments (but, it can be deployed on other containers too).

We will create the simple HelloWorldService in the following steps:

  1. In the NetBeans IDE, create a new project by selecting the "Java Web > Web Application" wizard.
  2. Name the project as HelloWorldWebapp.
  3. Set the server to GlassFish v2 as shown in the snapshot, and say Finish:  Select GlassFish v2 from the servers
  4. Right click on the project in the left pane, and select "New" > "Other". Select New > Other from the menu
  5. In the "Choose File Type" dialog, select "Web Services" and "RESTful Web Services from Patterns". Select Web Serv ices > RESTful Web Service from Patterns
  6. Set the pattern to Singleton in the next screen.
  7. In the next screen, set the package name to mycompany.resources. Set path to hello, class name to HelloResource and MIME-type to text/plain. Click the Finish button.
  8. The wizard creates the HelloResource class which has methods annotated with @GET and @PUT.
  9. Modify the @GET annotated method name to sayHello() and add the statement return "Hello World! Jersey welcome you";
  10. Have a look at the generated web.xml: it has a mapping of the Jersey SPI defined servlet com.sun.jersey.spi.container.servlet.ServletContainer to the url-patterns /resources/\* .
  11. Deploy the application.
  12. In a browser, enter the URL http://localhost:8080/HelloWorldWebapp/resources/hello. It gives a response with the string Hello World! Jersey welcome you.
  13. These are the steps for creating a resource using NetBeans and Jersey. 

Overview of the generated resource class

           It can be seen that the generated class has some annotations over the method implementations and the class definition. Lets see what these annotations are and what they mean:

  • @Path("hello") : This annotation describes the path to the resource, i.e., any request with the path "hello" would be routed to this resource.
  • @GET: This annotation maps the method sayHello to the HTTP GET request sent to the HelloResource.
  • @Produces("text/plain"): This annotation describes the mime type of data, which can be produced by the resource. If a resource supports multiple representations of data, all the supported mime-types could be declared as an array of strings in the Produces annotation. For ex: @Produces({"text/plain", "application/xml"}) means that the resource is capable of serving requests which have the Accept header set to either text/plain or application/xml.
  • @Consumes("text/plain"): This annotation describes the mime type of data which a resource can consume. Similar to the Produces annotation if the resource is capable of consuming multiple representations of data, all these representations could be declared as an array of strings in the Consumes annotation.
  • @PUT: This annotation maps the annotated method to the HTTP PUT request sent to the resource.

In the next entries we will see how to use the other features provided by JAX-RS/Jersey to access Uri Parameters, Query Parameters, Client API, etc. Also we will see how to deploy the applications to light weight HTTPServer, GrizzlyWebContainer and EmbeddedGlassFish.

Thursday Dec 04, 2008

Introducing Jersey

Jersey is the open source production quality reference implementation of JAX-RS for building RESTful web services. Jersey provides an API for building and consuming RESTful web services.

Jersey is built, assembled and installed using Maven.

Jersey's runtime dependencies are categorized into the following:

  • Core server. The minimum set of dependences that Jersey requires for the server.

  • Core client. The minimum set of dependences that Jersey requires for the client.

  • Container. The set of container dependences. Each container provider has it's own set of dependences.

  • Entity. The set of entity dependencies. Each entity provider has it's own set of dependences.

  • Tools. The set of dependencies required for runtime tooling.

  • Spring. The set of dependencies required for Spring.

    More information on these dependencies is available at the Jersey dependencies page.

Thursday Nov 27, 2008

JAX-RS: Java API for RESTful web services


[Read More]

An introduction to RESTful Web Services

What is REST?

        REST is an acronym for one popular architectural style, Representational State Transfer. It is an architectural style for building distributed software systems like the WWW. It is modeled after the way data is represented, accessed and modified on the web. This is based on Chapter 5 of Roy Fielding's PhD dissertation.

In the REST architecture, data and functionality are considered resources, and these resources are accessed using URIs. The core principle of REST is - 'Everything is a resource'. For the same reason, it is quiet often referred to as "resource-oriented architecture".

RESTful Web Services

     RESTful Web Services are web services which are built on the REST architecture. These services expose the resources through web URIs. The following HTTP methods are used to create, retrieve, update and delete (CRUD) resources:

  • POST
  • GET
  • PUT
  • DELETE

   RESTful web services might be programmed in such a way that they serve data of various mime-types. For instance, they might take requests and respond with following types of data depending on the accept-type request header:

  • Plain-Text (text/plain)
  • HTML (text/html)
  • JavaScript Object Notation (application/json)
  • XML (application/xml)

RESTful Web Services vs SOAP Web Services

RESTful  SOAP
 ROA - Resource oriented architecture
 SOA - SOAP oriented architecture
 Response is encapsulated in HTTP envelope.
 Response is encapsulated in SOAP envelope, which in turn is encapsulated in HTTP envelope.
WADL describes the web service.
WSDL describes the web service.
 Light weight, easy to develop.
 Heavy weight, complex.
 Tied to HTTP protocol
 Built on RPC

About

Naresh worked at Sun Microsystems for two years. During these two years he had worked on the Project Metro and Project Jersey.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today