Tuesday Jul 23, 2013

JSON Binding with JAX-RS - Moxy, JSON-P, Jackson, Jettison (TOTD #214)


One of the typical questions asked during presentations is how do I enable JSON support in JAX-RS ?

There are three basic approaches and four different modules in Jersey that enable them. Here is a quick summary:


MOXy
JSON-P
Jackson
Jettison
POJO-based JSON Binding
Yes
No
Yes
No
JAXB-based JSON Binding
Yes
No
Yes
Yes
Low-level JSON parsing & processing
No
Yes
No
Yes

MOXy is the default and preferred way of supporting JSON binding in your Jersey applications. A new sample is now added at:

https://github.com/arun-gupta/javaee7-samples/tree/master/jaxrs/moxy

The resource definition is:

@Path("endpoint")
public class MyResource {
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public MyObject echoObject(MyObject mo) {
        return mo;
    }
}


POJO is defined as:

public class MyObject {

    private String name;
    private int age;

    public MyObject() {
    }

    public MyObject(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Application class is:

@ApplicationPath("webresources")
public class MyApplication extends Application {
}

And the client code is:

Client client = ClientBuilder.newClient();

WebTarget target = client.target("http://"
        + request.getServerName()
        + ":"
        + request.getServerPort()
        + request.getContextPath()
        + "/webresources/endpoint");
System.out.println("POST request");
MyObject mo = target
        .request()
        .post(Entity.entity(new MyObject("Duke", 18), MediaType.APPLICATION_JSON), MyObject.class);
out.println("Received response: " + mo.getName() + ", " + mo.getAge() + "<br><br>");

JSON MOXy module (jersey-media-moxy.jar) is in your classpath then Jersey automatically discovers the module and enable JSON binding support via MOXy.

jersey.java.net/documentation/latest/media.html provide more details on all the approaches and modules.

Monday Mar 04, 2013

Consuming and Producing JSON using JAX-RS Entity Providers and JSR 353 Streaming API (TOTD# 210)


TOTD #193 explained how JAX-RS Entity Providers can be used to provide mapping between on-the-wire representations and their associated Java types. This blog shows how you can use Java API for JSON Processing (JSR 353), already integrated in GlassFish 4 promoted builds, to produce and consume JSON payload using Entity Providers.

The source code in this blog can be downloaded here and runs on GlassFish b76.

Lets say your domain object is defined as:

public class MyObject {

  private String name;
  private int age;

  //. . .
}

And your resource endpoint is defined as:

@Path("endpoint")
public class MyResource {
  @POST
  @Consumes(MediaType.APPLICATION_JSON)
  public MyObject echoObject(MyObject mo) {
    return mo;
  }
}

This is just echoing the domain object but I suspect your domain logic would be more complex than that ;-)

Using JAX-RS Client API, this endpoint can be invoked as:

WebTarget target = client.target(".../endpoint");
MyObject mo = target
               .request()
               .post(
                 Entity.entity(new MyObject("Duke", 18), MediaType.APPLICATION_JSON),
                 MyObject.class);
System.out.println("Received response: " + mo.getName() + ", " + mo.getAge() + "<br><br>");

The MessageBodyWriter.writeTo method, that writes MyObject to the underlying OutputStream, uses Streaming API from JSR 353 and looks like:

@Override
public void writeTo(MyObject t,
                    Class<?> type,
                    Type type1,
                    Annotation[] antns,
                    MediaType mt,
                    MultivaluedMap<String, Object> mm,
                    OutputStream out)
            throws IOException, WebApplicationException {
  JsonGeneratorFactory factory = Json.createGeneratorFactory();
  JsonGenerator gen = factory.createGenerator(out);
  gen.writeStartObject()
     .write("name", t.getName())
     .write("age", t.getAge())
     .writeEnd();
  gen.flush();
}

Similarly MessageBodyReader.readFrom method, that reads MyObject from the underlying InputStream, uses Streaming API from JSR 353 and looks like:

@Override
public MyObject readFrom(Class<MyObject> type,
                         Type type1,
                         Annotation[] antns,
                         MediaType mt,
                         MultivaluedMap<String, String> mm,
                         InputStream in)
                throws IOException, WebApplicationException {
  MyObject mo = new MyObject();
  JsonParser parser = Json.createParser(in);
  while (parser.hasNext()) {
    switch (parser.next()) {
      case KEY_NAME:
        String key = parser.getString();
        parser.next();
        switch (key) {
          case "name":
            mo.setName(parser.getString());
            break;
          case "age":
            mo.setAge(parser.getIntValue());
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }
  return mo;
}

The code is pretty straight forward and refer to Java API for JSON Processing javadocs if you need help in understanding the code.

Download the source code and enjoy!

Tuesday Feb 19, 2013

JsonParser and JsonReader - Streaming and Object Readers in Java API for JSON Processing (TOTD #206)


JsonReader reads a JSON object or array from an input source. This provides DOM access to the JSON structure.

JsonParser provides forward, ready-only access to JSON data in a streaming way. The parser can be created from InputStream and Reader. A sample code looks like:

JsonParser jsonParser = Json.createParser(new StringReader(YOUR_STRING_HERE));

Here is a table with code fragments for parsing some common JSON using Object Model and events generated from Streaming API:

JSON Object Model API Events from Streaming API
{ }
JsonReader jsonReader =
    new JsonReader(new StringReader("{}"));
JsonObject json = jsonReader.readObject();
{START_OBJECT }END_OBJECT
{
  "apple":"red",
  "banana":"yellow"
}
jsonReader =
    new JsonReader(new StringReader(...));
json = jsonReader.readObject();

{START_OBJECT
  "apple"KEY_NAME:"red"VALUE_STRING,
  "banana"KEY_NAME:"yellow"VALUE_STRING
}
[
  { "apple":"red" },
  { "banana":"yellow" }
]
jsonReader =
    new JsonReader(new StringReader(...));
JsonArray jsonArr = jsonReader.readArray();

[START_ARRAY
  {START_OBJECT "apple"KEY_NAME:"red"VALUE_STRING }END_OBJECT,
  {START_OBJECT "banana"KEY_NAME:"yellow"VALUE_STRING }END_OBJECT
]END_ARRAY
{
  "title":"The Matrix",
  "year":1999,
  "cast":[
    "Keanu Reaves",
    "Laurence Fishburne",
    "Carrie-Anne Moss"
  ]
}
jsonReader =
    new JsonReader(new StringReader(...));
json = jsonReader.readObject();

{START_OBJECT
  "title"KEY_NAME:"The Matrix"VALUE_STRING,
  "year"KEY_NAME:1999VALUE_NUMBER,
  "cast"KEY_NAME:[START_ARRAY
    "Keanu Reaves"VALUE_STRING,
    "Laurence Fishburne"VALUE_STRING,
    "Carrie-Anne Moss"VALUE_STRING
  ]END_ARRAY
}END_OBJECT

The source code for this sample can be downloaded here, try it with GlassFish 4 b76 or later.

Note, GlassFish b76 has Public Review version of the APIs. But the APIs have evolved since then and an integration is planned in the next few days. So if you are using an older build, the sample code may not work. I'll update the sample code once the bits are integrated.

Here are some more links for you to read further:

Monday Feb 18, 2013

JsonGenerator and JsonObjectBuilder - Streaming and Object API in Java API for JSON Processing (TOTD #205)


Java API for JSON Processing (JSR 353) cleared Public Review unanimously and is on its way to to standardization. There is still Proposed Final Draft and the final vote to come. As per the Java EE 7 schedule, the specification will be final on 4/15/2013. The implementation is already integrated in GlassFish 4 builds.

The API provides an Object Model (like DOM for XML) and Streaming API (like StAX for XML) to parse and generate JSON structure. Here is a table that provide code fragments for generating some common JSON:

JSON Object Model API Streaming API
{ }
JsonObject jsonObject =
     new JsonObjectBuilder().build();

new JsonWriter(System.out)
     .writeObject(jsonObject);

JsonGeneratorFactory factory =
     Json.createGeneratorFactory();

JsonGenerator gen =
     factory.createGenerator(System.out);

gen.writeStartObject().writeEnd();
{
  "apple":"red",
  "banana":"yellow"
}
new JsonObjectBuilder()
  .add("apple", "red")
  .add("banana", "yellow")
.build();
gen.writeStartObject()
     .write("apple", "red")
     .write("banana", "yellow")
   .writeEnd();
[
  { "apple":"red" },
  { "banana":"yellow" }
]
JsonArray jsonArray = new JsonArrayBuilder()
  .add(new JsonObjectBuilder()
          .add("apple","red"))

  .add(new JsonObjectBuilder()
          .add("banana","yellow"))

  .build();
gen.writeStartArray()
     .writeStartObject()
       .write("apple", "red")
     .writeEnd()
     .writeStartObject()
       .write("banana", "yellow")
     .writeEnd()
.writeEnd();
{
  "title":"The Matrix",
  "year":1999,
  "cast":[
    "Keanu Reaves",
    "Laurence Fishburne",
    "Carrie-Anne Moss"
  ]
}
new new JsonObjectBuilder()
  .add("title", "The Matrix")
  .add("year", 1999)
  .add("cast", new JsonArrayBuilder()
  .add("Keanu Reaves")
  .add("Laurence Fishburne")
  .add("Carrie-Anne Moss"))
.build();
gen.writeStartObject()
     .write("title", "The Matrix")
     .write("year", 1999)
     .writeStartArray("cast")
       .write("Keanu Reaves")
       .write("Laurence Fishburne")
       .write("Carrie-Anne Moss")
     .writeEnd()
   .writeEnd();

The source code for this sample can be downloaded here, try it with GlassFish 4 b76 or later.

Here are some more links for you to read further:

Tuesday May 15, 2012

JSON-P: Java API for JSON Processing (TOTD #178)


JSR 353 is Java API for JSON Processing (JSON-P) and will define an API to process (e.g. parse, generate, transform, and query) JSON. This JSR will be delivered as part of Java EE 7. The API will allow to produce and consume JSON in a streaming fashion (StAX equivalent in XML world) and build a Java object model for JSON (DOM equivalent in XML world). Note, binding JSON to Java objects and vice versa is not part of the scope of this JSR.

json-processing-spec.java.net is where all the specification work is happening and jsonp.java.net is the project for the  Reference Implementation. Its still early days but this Tip Of The Day (TOTD) will explain how to get started. The workspace can be checked out as

git clone git://java.net/jsonp~git
Cloning into jsonp~git...
remote: Counting objects: 313, done.
remote: Compressing objects: 100% (218/218), done.
remote: Total 313 (delta 110), reused 0 (delta 0)
Receiving objects: 100% (313/313), 64.13 KiB, done.
Resolving deltas: 100% (110/110), done.

Building the workspace require JAVA_HOME to be set (/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home or /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home). Once set, the workspace can be built as
mvn install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] RI for JSON-P JSR
[INFO] Java API for Processing JSON (JSON-P)
[INFO] jsonp-tests
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building RI for JSON-P JSR 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------

. . .

Tests run: 22, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ jsonp-tests ---
[WARNING] JAR will be empty - no content was marked for inclusion!
[INFO] Building jar: /Users/arungup/code/workspaces/jsonp~git/tests/target/jsonp-tests-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.3.1:install (default-install) @ jsonp-tests ---
[INFO] Installing /Users/arungup/code/workspaces/jsonp~git/tests/target/jsonp-tests-1.0-SNAPSHOT.jar to /Users/arungup/.m2/repository/org/glassfish/jsonp-tests/1.0-SNAPSHOT/jsonp-tests-1.0-SNAPSHOT.jar
[INFO] Installing /Users/arungup/code/workspaces/jsonp~git/tests/pom.xml to /Users/arungup/.m2/repository/org/glassfish/jsonp-tests/1.0-SNAPSHOT/jsonp-tests-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] RI for JSON-P JSR ................................. SUCCESS [0.528s]
[INFO] Java API for Processing JSON (JSON-P) ............. SUCCESS [12.214s]
[INFO] jsonp-tests ....................................... SUCCESS [1.695s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

The API .jar file is in api/target/jsonp-ri-1.0-SNAPSHOT.jar and javadocs are in api/target/jsonp-ri-1.0-SNAPSHOT-javadoc.jar. There are several tests in the tests directory that shows the API usage. The RI JARs are not pushed to a public maven repo yet but are installed in the local repo with the above command. These can be included in your "pom.xml" with the following coordinates:

<dependency>
<groupId>javax.json</groupId>
<artifactId>jsonp-ri</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

The key APIs are
  • DOM-based APIs (javax.json package)
    • JsonBuilder - Builds a JSON object or JSON array
    • JsonReader - Reads a JSON object or array from the stream
    • JsonWriter - Writes a JSON object or array to the stream
  • Streaming APIs (javax.json.stream package)
    • JsonGenerator - Streaming JSON generator
    • JsonParser - Allows forward, read-only access to JSON

Here is an sample usage of JsonBuilder:
JsonObject value = new JsonBuilder()
   .beginObject()
     .add("firstName", "John")
     .add("lastName", "Smith")
     .add("age", 25)
        .beginObject("address")
           .add("streetAddress", "21 2nd Street")
           .add("city", "New York")
           .add("state", "NY")
           .add("postalCode", "10021")
        .endObject()
        .beginArray("phoneNumber")
           .beginObject()
              .add("type", "home")
              .add("number", "212 555-1234")
           .endObject()
           .beginObject()
              .add("type", "home")
              .add("number", "646 555-4567")
           .endObject()
        .endArray()
   .endObject()
.build();

Here is a sample usage of JsonReader:

String json = "...";
JsonReader reader = new JsonReader(new StringReader(json));
JsonValue value = reader.readObject();
reader.close();

A sample usage of JsonWriter:

JsonWriter jsonWriter = new JsonWriter(new FileWriter(...));
JsonObject jsonObject = new JsonBuilder()
.beginObject()
. . .
.endObject()
.build()
; jsonWriter.writeObject(jsonObject); jsonWriter.close();
Here is a sample usage of JsonGenerator:

JsonGenerator generator = new JsonGenerator(new FileWriter(...));
generator
.beginObject()
. . .
.beginArray()
. . .
.endArray()
.endObject()
.build();
generator.close();

And finally a sample usage of JsonParser:
String json = "...";
JsonParser parser = new JsonParser(new StringReader(json));
Iterator<Event> it = reader.iterator();
Event event = it.next();
The event  can be of the following types:
  • START_OBJECT
  • END_OBJECT
  • START_ARRAY
  • END_ARRAY
  • KEY_NAME
  • VALUE_STRING
  • VALUE_NUMBER
  • VALUE_FALSE
  • VALUE_TRUE
  • VALUE_NULL
Here are some references to track the progress and provide feedback:
This JSR will be delivered as part of Java EE 7. Here are some other early work that has been explained:

Have fun!

Thursday Aug 06, 2009

TOTD #91: Retrieve JSON libraries using Maven dependency: json-lib


So you need to include JSON libraries in your Maven project. The only option that seems to be currently available is using json-lib with the following dependencies:

        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.3</version>
            <classifier>jdk15</classifier>
        </dependency>

The APIs are based upon the original work done at json.org/java.

If you are using NetBeans for adding the Maven dependency then it nicely shows the different versions for the artifact as shown below:



The usage guide and samples at json-lib have lots of documentation to get you started. Don't forget the package names are changed so "org.json.JSONObject" is "net.sf.json.JSONObject" and similarly other classes.

Please leave suggestions on other TOTD (Tip Of The Day) that you'd like to see. A complete archive of all the tips is available here.

Technorati: json maven json-lib netbeans

Sunday Nov 30, 2008

TOTD #58: Jersey and GlassFish - how to process POST requests ?


Lets extend the Jersey endpoint (TOTD# 56) and client (TOTD# 57) such that it can accept a POST request and then invoke it.
  1. Add a new method to "MyResource.java" from TOTD# 56 as:

        @POST
        @Consumes("application/json")
        @Produces("application/json")
        public Greeting postIt(Greeting greeting) {
            System.out.println("In POST: " + greeting.greeting);
            return greeting;
        }

    The first line indicates that the Java method will process HTTP POST requests. The second and third line shows that the method consumes and produces JSON data format.
  2. Add a new method to "AppTest.java" from TOTD# 57 as:

        public void testPost() {
            Greeting result = createResource().
                    type("application/json").
                    post(Greeting.class, new Greeting("yo!"));
            assertTrue(result.greeting.equals("yo!"));
        }

    The main difference from the "testApp()" method is specifying the MIME type of the generated outbound request as "application/json".
  3. Running the test as "mvn test" shows the following output:

    Running org.glassfish.samples.AppTest
    1 \* Out-bound request
    1 > GET http://localhost:8080/helloworld-webapp/webresources/myresource
    1 >
    1 < 200
    1 < X-Powered-By: Servlet/2.5
    1 < Transfer-Encoding: chunked
    1 < Content-Type: text/plain
    1 < Server: GlassFish/v3
    1 < Date: Tue, 25 Nov 2008 20:19:34 GMT
    1 <
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?><greeting><greeting>Hi there!</greeting></greeting>
    1 \* In-bound response
    1 \* Out-bound request
    1 > POST http://localhost:8080/helloworld-webapp/webresources/myresource
    1 > Content-Type: application/json
    1 >
    {"greeting":"yo!"}
    1 < 200
    1 < X-Powered-By: Servlet/2.5
    1 < Transfer-Encoding: chunked
    1 < Content-Type: application/json
    1 < Server: GlassFish/v3
    1 < Date: Tue, 25 Nov 2008 20:19:34 GMT
    1 <
    {"greeting":"yo!"}
    1 \* In-bound response
    Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.191 sec

    The output shows request/response messages when both the tests are run together. Here are some highlights:
    1. "GET" and "POST" methods are clearly highlighted.
    2. The two "Content-Type" headers with value "text/plain" and "application/json" are output from two tests. The output from POST method has two Content-Type headers, one for outbound request and another one for inbound response.
    3. The body content of POST method is using JSON format.
Jersey and GlassFish provides a complete server-side and client-side API and framework for deploying and invoking RESTful Web service endpoints.

How are you using Jersey ?

Send all your questions to users@jersey.dev.java.net.

Please leave suggestions on other TOTD (Tip Of The Day) that you'd like to see. An archive of all the tips is available here.

Technorati: totd glassfish v3 embeddable jersey jsr311 rest json webservices

Tuesday Nov 25, 2008

TOTD #57: Jersey Client API - simple and easy to use

TOTD #56 explains how to create a RESTful Web service endpoint using Jersey and publish the resource using JSON representation. The blog entry showed how the endpoint can be accessed from a Web browser. This Tip Of The Day explains how to use Jersey Client APIs to invoke the published endpoint.

Lets get started!
  1. Create a new directory "./src/test/java/org/glassfish/samples"
  2. Add a test
    1. Add a template test file "AppTest.java" as shown below:

      package org.glassfish.samples;

      import junit.framework.Test;
      import junit.framework.TestCase;
      import junit.framework.TestSuite;

      /\*\*
       \* Unit test for simple App.
       \*/
      public class AppTest
          extends TestCase
      {
          /\*\*
           \* Create the test case
           \*
           \* @param testName name of the test case
           \*/
          public AppTest( String testName )
          {
              super( testName );
          }

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

          /\*\*
           \* Rigourous Test :-)
           \*/
          public void testApp()
          {
              assertTrue(true);
          }
      }
    2. Add a new method "createResource()" as:

          private WebResource createResource() {
              Client client = Client.create();
              WebResource resource = client.resource("http://localhost:8080/helloworld-webapp/webresources/myresource");
              return resource;
          }

      This code creates a default instance of Jersey Client and creates a Web resource from that client for the URI passed as an argument.
    3. Change the implementation of "testApp()" method as:

              Greeting result = createResource().get(Greeting.class);
              assertTrue(result.greeting.equals("Hi there!"));

      This invokes the GET method on the resource by passing specific type and compares the returned and expected value.
    4. Add the following "imports":

      import com.sun.jersey.api.client.Client;
      import com.sun.jersey.api.client.WebResource;
    5. Copy "Greeting.java" from TOTD #56 to "./src/test/java/org/glassfish/samples" directory.
  3. Run the test
    1. Deploy the endpoint as "mvn glassfish:run".
    2. Run the test as "mvn test". The following output is shown:

      ~/samples/jersey/helloworld-webapp >mvn test
      [INFO] Scanning for projects...
      [INFO] ------------------------------------------------------------------------
      [INFO] Building helloworld-webapp Jersey Webapp
      [INFO]    task-segment: [test]
      [INFO] ------------------------------------------------------------------------
      [INFO] [resources:resources]
      [INFO] Using default encoding to copy filtered resources.
      [INFO] [compiler:compile]
      [INFO] Nothing to compile - all classes are up to date
      [INFO] [resources:testResources]
      [INFO] Using default encoding to copy filtered resources.
      [INFO] [compiler:testCompile]
      [INFO] Compiling 1 source file to /Users/arungupta/samples/jersey/helloworld-webapp/target/test-classes
      [INFO] [surefire:test]
      [INFO] Surefire report directory: /Users/arungupta/samples/jersey/helloworld-webapp/target/surefire-reports

      -------------------------------------------------------
       T E S T S
      -------------------------------------------------------
      Running org.glassfish.samples.AppTest
      Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.587 sec

      Results :

      Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD SUCCESSFUL
      [INFO] ------------------------------------------------------------------------
      [INFO] Total time: 4 seconds
      [INFO] Finished at: Mon Nov 24 16:50:17 PST 2008
      [INFO] Final Memory: 18M/43M
      [INFO] ------------------------------------------------------------------------
  4. View request and response messages
    1. Change the implementation of "createResource()" method as (changes highlighted in bold):

              Client client = Client.create();
              WebResource resource = client.resource("http://localhost:8080/helloworld-webapp/webresources/myresource");
              resource.addFilter(new LoggingFilter());
              return resource;
    2. Running the tests as "mvn test" now shows the output, with request and response messages, as shown below:

      Running org.glassfish.samples.AppTest
      1 \* Out-bound request
      1 > GET http://localhost:8080/helloworld-webapp/webresources/myresource
      1 >
      1 < 200
      1 < X-Powered-By: Servlet/2.5
      1 < Transfer-Encoding: chunked
      1 < Content-Type: application/json
      1 < Server: GlassFish/v3
      1 < Date: Tue, 25 Nov 2008 07:07:51 GMT
      1 <
      {"greeting":"Hi there!"}
      1 \* In-bound response
      Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.074 sec
Really easy!

Even though the APIs are used to invoke a RESTful endpoint deployed using Jersey but are very generic and can be used to invoke any RESTful endpoint. Paul's blog explain in detail on the usage. You can also see how these APIs can be used to consume a service hosted using Apache Abdera.

com.sun.jersey.api.client, com.sun.jersey.api.client.config, and com.sun.jersey.api.client.filter packages documents all the classes that provide support for client-side communication with HTTP-based RESTful Web services.

Technorati: totd glassfish v3 embeddable jersey jsr311 rest json webservices

Monday Nov 24, 2008

TOTD #56: Simple RESTful Web service using Jersey and Embeddable GlassFish - Text and JSON output


Jersey is the open source, production quality, JAX-RS (JSR 311) Reference Implementation for building RESTful Web services in the GlassFish community. It also provides an API that allows developers to extend Jersey to suite their requirements.

This Tip Of The Day (TOTD) shows how to create a simple RESTful Web service using Jersey and run it using embeddable GlassFish (glassfish:run). Maven is used to create and run the application. It also shows how the output format can be easily coverted from Text to JSON.

Lets get started!
  1. Create a simple web app using Maven as:

    ~/samples/jersey >mvn archetype:generate -DarchetypeCatalog=http://download.java.net/maven/2
    [INFO] Scanning for projects...
    [INFO] Searching repository for plugin with prefix: 'archetype'.
    [INFO] ------------------------------------------------------------------------
    [INFO] Building Maven Default Project
    [INFO]    task-segment: [archetype:generate] (aggregator-style)
    [INFO] ------------------------------------------------------------------------
    [INFO] Preparing archetype:generate
    [INFO] No goals needed for project - skipping
    [INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.
    [INFO] Setting property: velocimacro.messages.on => 'false'.
    [INFO] Setting property: resource.loader => 'classpath'.
    [INFO] Setting property: resource.manager.logwhenfound => 'false'.
    [INFO] [archetype:generate]
    [INFO] Generating project in Interactive mode
    [INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
    Choose archetype:
    1: remote -> jersey-quickstart-grizzly (Archetype for creating a RESTful web application with Jersey and Grizzly)
    2: remote -> jersey-quickstart-webapp (Archetype for creating a Jersey based RESTful web application WAR packaging)
    Choose a number:  (1/2): 2
    [INFO] snapshot com.sun.jersey.archetypes:jersey-quickstart-webapp:1.0.1-SNAPSHOT: checking for updates from jersey-quickstart-webapp-repo
    Define value for groupId: : org.glassfish.samples
    Define value for artifactId: : helloworld-webapp
    Define value for version:  1.0-SNAPSHOT: :
    Define value for package: : org.glassfish.samples
    Confirm properties configuration:
    groupId: org.glassfish.samples
    artifactId: helloworld-webapp
    version: 1.0-SNAPSHOT
    package: org.glassfish.samples
     Y: :
    [INFO] ----------------------------------------------------------------------------
    [INFO] Using following parameters for creating OldArchetype: jersey-quickstart-webapp:1.0.1-SNAPSHOT
    [INFO] ----------------------------------------------------------------------------
    [INFO] Parameter: groupId, Value: org.glassfish.samples
    [INFO] Parameter: packageName, Value: org.glassfish.samples
    [INFO] Parameter: package, Value: org.glassfish.samples
    [INFO] Parameter: artifactId, Value: helloworld-webapp
    [INFO] Parameter: basedir, Value: /Users/arungupta/samples/jersey
    [INFO] Parameter: version, Value: 1.0-SNAPSHOT
    [INFO] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* End of debug info from resources from generated POM \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
    [INFO] OldArchetype created in dir: /Users/arungupta/samples/jersey/helloworld-webapp
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 21 seconds
    [INFO] Finished at: Mon Nov 24 14:09:27 PST 2008
    [INFO] Final Memory: 12M/30M
    [INFO] ------------------------------------------------------------------------
  2. Edit the generated "pom.xml" to add dependencies on GlassFish plugin
    1. Add the following plugin in the "pom.xml" under <build>/<plugins>:

                  <plugin>
                      <groupId>org.glassfish</groupId>
                      <artifactId>maven-glassfish-plugin</artifactId>
                  </plugin>
    2. Add the following plugin repositories:

          <pluginRepositories>
              <pluginRepository>
                  <id>maven2-repository.dev.java.net</id>
                  <name>Java.net Repository for Maven</name>
                  <url>http://download.java.net/maven/2/</url>
                  <layout>default</layout>
              </pluginRepository>
              <pluginRepository>
                  <id>maven-repository.dev.java.net</id>
                  <name>Java.net Maven 1 Repository (legacy)</name>
                  <url>http://download.java.net/maven/1</url>
                  <layout>legacy</layout>
              </pluginRepository>
          </pluginRepositories>
    3. Optionally, if the generated dependencies in "pom.xml" as shown below:

              <dependency>
                  <groupId>org.glassfish.distributions</groupId>
                  <artifactId>web-all</artifactId>
                  <version>10.0-build-20080430</version>
                  <scope>test</scope>
              </dependency>
              <dependency>
                  <groupId>org.glassfish.embedded</groupId>
                  <artifactId>gf-embedded-api</artifactId>
                  <version>1.0-alpha-4</version>
                  <scope>test</scope>
              </dependency>

      are changed to:

              <dependency>
                  <groupId>org.glassfish.distributions</groupId>
                  <artifactId>web-all</artifactId>
                  <version>10.0-SNAPSHOT</version>
                  <scope>test</scope>
              </dependency>
              <dependency>
                 <groupId>org.glassfish.embedded</groupId>
                 <artifactId>glassfish-embedded-all</artifactId>
                 <version>3.0-Prelude-SNAPSHOT</version>
              </dependency>

      then the latest version of Embedded GlassFish APIs are used.
    4. Also optionally, if you want to run against Jersey 1.0 bits then change the following property from "1.0.1-SNAPSHOT" to "1.0".

          <properties>
              <jersey-version>1.0</jersey-version>
          </properties>
  3. Run the application
    1. The generated source code is:

      package org.glassfish.samples;

      import javax.ws.rs.GET;
      import javax.ws.rs.Path;
      import javax.ws.rs.Produces;

      // The Java class will be hosted at the URI path "/helloworld"
      @Path("/myresource")
      public class MyResource {
         
          // The Java method will process HTTP GET requests
          @GET
          // The Java method will produce content identified by the MIME Media
          // type "text/plain"
          @Produces("text/plain")
          public String getIt() {
              return "Hi there!";
          }
      }

      Invoking "mvn glassfish:run" starts the embedded GlassFish and shows the following output:

      ~/samples/jersey/helloworld-webapp >mvn glassfish:run
      [INFO] Scanning for projects...
      [INFO] Searching repository for plugin with prefix: 'glassfish'.
      [INFO] ------------------------------------------------------------------------
      [INFO] Building helloworld-webapp Jersey Webapp
      [INFO]    task-segment: [glassfish:run]
      [INFO] ------------------------------------------------------------------------
      [INFO] Preparing glassfish:run
      [INFO] [resources:resources]
      [INFO] Using default encoding to copy filtered resources.
      [INFO] [compiler:compile]
      [INFO] Compiling 1 source file to /Users/arungupta/samples/jersey/helloworld-webapp/target/classes
      [INFO] [glassfish:run]
      Nov 24, 2008 2:36:05 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: HK2 initialized in 229 ms
      Nov 24, 2008 2:36:05 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: com.sun.enterprise.naming.impl.ServicesHookup@2470b02c Init done in 237 ms
      Nov 24, 2008 2:36:05 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: com.sun.enterprise.v3.server.Globals@13b3d787 Init done in 239 ms
      Nov 24, 2008 2:36:05 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: com.sun.enterprise.v3.server.SystemTasks@61bedd7d Init done in 244 ms
      Nov 24, 2008 2:36:05 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: com.sun.enterprise.v3.services.impl.HouseKeeper@2b9f7952 Init done in 245 ms
      Nov 24, 2008 2:36:05 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: com.sun.enterprise.v3.services.impl.CmdLineParamProcessor@5249d560 Init done in 248 ms
      JMXMP connector server URL = service:jmx:jmxmp://localhost:8888
      Nov 24, 2008 2:36:05 PM com.sun.enterprise.v3.services.impl.GrizzlyProxy start
      INFO: Listening on port 8080
      Nov 24, 2008 2:36:06 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: com.sun.enterprise.v3.services.impl.GrizzlyService@1baa56a2 startup done in 551 ms
      Nov 24, 2008 2:36:06 PM com.sun.enterprise.v3.services.impl.ApplicationLoaderService postConstruct
      INFO: loader service postConstruct started at 1227566166208
      Nov 24, 2008 2:36:06 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: Application Loader startup done in 740 ms
      Nov 24, 2008 2:36:06 PM com.sun.enterprise.v3.server.AppServerStartup run
      INFO: Glassfish v3 started in 740 ms
      Nov 24, 2008 2:36:07 PM com.sun.enterprise.web.WebModuleContextConfig authenticatorConfig
      SEVERE: webModuleContextConfig.missingRealm
      Nov 24, 2008 2:36:07 PM com.sun.jersey.api.core.PackagesResourceConfig init
      INFO: Scanning for root resource and provider classes in the packages:
        org.glassfish.samples
      Nov 24, 2008 2:36:07 PM com.sun.jersey.api.core.PackagesResourceConfig init
      INFO: Root resource classes found:
        class org.glassfish.samples.MyResource
      Nov 24, 2008 2:36:07 PM com.sun.jersey.api.core.PackagesResourceConfig init
      INFO: Provider classes found:
      Hit ENTER for redeploy

      Notice how GlassFish v3 starts up in sub-second (740 ms in this case).
    2. "http://localhost:8080/helloworld-webapp" shows the following output:

    3. Clicking on "Jersey resource" redirects to "http://localhost:8080/helloworld-webapp/webresources/myresource" and shows the following output:

  4. Change the output representation to produce JSON representation
    1. Add a new JAXB bean:

      package org.glassfish.samples;

      import javax.xml.bind.annotation.XmlRootElement;

      /\*\*
       \* @author arungupta
       \*/
      @XmlRootElement
      public class Greeting {
          public String greeting;

          public Greeting() { }
          public Greeting(String greeting) {
              this.greeting = greeting;
          }
      }
    2. Change the method implementation in MyResource as:

      //    @Produces("text/plain")
          @Produces("application/json")
          public Greeting getIt() {
              return new Greeting("Hi there!");
          }
    3. And now "http://localhost:8080/helloworld-webapp/webresources/myresource" shows the following output:



      Notice the output is now in JSON format.
  5. Optionally a WAR file can be created using the command:

    mvn clean package

    and the WAR file is generated in "target/helloworld-webapp.war". If Jersey is installed using GlassFish v3 Update Center then you can use "maven-assembly-plugin" to customize packaging of WAR and drastically reduce the size.
The JSON representation can be configured in multiple ways as explained in Configuring JSON for RESTful Web Services in Jersey 1.0. This has certainly come a long way from TOTD #8 and is much more effecient now.

The Jersey Wiki documents an extensive set of resources to get started.

Send all your questions to users@jersey.dev.java.net.

Please leave suggestions on other TOTD (Tip Of The Day) that you'd like to see. An archive of all the tips is available here.

Technorati: totd glassfish v3 embeddable jersey jsr311 rest json webservices

Tuesday Oct 02, 2007

TOTD #10: Consuming JSON and XML representations generated by a Jersey endpoint in a jMaki Table widget

A jMaki widget expects data in JSON format as defined by the standard data models. There are three possible ways to generate the JSON data from a Jersey endpoint that can be consumed by a jMaki widget:

  1. Return JSON representation of a resource as generated using the BadgerFish convention and then apply a stylesheet (specified in xhp.json) to convert the received data into the JSON format as expected by the jMaki widget. This keeps the server code simple but requires a stylesheet to convert from one JSON format (defined by BadgerFish convention) to another JSON format (as expected by the jMaki widget).
  2. Return XML representation of the resource and then apply a stylesheet (specified in xhp.json) to convert the received JSON into the format as expected by the jMaki widget. This keeps the server code simple but requires a stylesheet to convert from the XML format (defined by JAXB) to JSON format (as expected by the jMaki widget).
  3. Return JSON representation as expected by the jMaki widget. This can be achieved only using low-level JSON APIs on the server-side and can be directly consumed by the jMaki widget.

This TOTD explains 2nd and 3rd bullet. The first bullet can be applied following the solution proposed in 2nd bullet.

  1. Download and expand the latest Jersey download.
  2. Download and install GlassFish V2. This will be required for deploying the jMaki project and using Database client and Persistence libraries.
  3. Open "examples/HelloWorld" project in NetBeans 6 IDE.
  4. In the NetBeans IDE, Services tab, expand Databases, right-click on the node with URL "jdbc:derby://localhost:1527/sample [app on APP]" and select Connect. Enter the password as "app" and select "OK".
  5. Configure the database
    1. Right-click again on the URL and select "Execute Command..." and issue the command:

      create table BOOKS (title varchar(255),
                          author varchar(255),
                          isbn varchar(255),
                          description varchar(255),
                          PRIMARY KEY (isbn))


      This will create the database table.
    2. Add data to the newly created table using the following command:

      INSERT INTO BOOKS VALUES('Marathon', 'Jeff Galloway', 'ABCDEF', 'The best book on running');
      INSERT INTO BOOKS VALUES('Run a Marathon', 'Duke', '123456', 'How to train for a marathon ?');


      You can enter additional rows if you like.
  6. Create the Persistence Unit
    1. In NetBeans IDE, right-click on the project, select "New", "Entity Classes from Database...".
    2. Choose the Database Connection with the URL given above.
    3. In the "Available Tables", select "BOOKS" and click on "Add >". Click on "Next >".
    4. Click on "Create Persistence Unit...", take all the defaults and click on "Create" and then click on "Finish".
    5. Add the following NamedQuery "@NamedQuery(name = "Books.findAll", query = "SELECT b FROM Books b")" to the generated Books class.
    6. Add the following annotation "@javax.xml.bind.annotation.XmlRootElement" to the generated Books class.
  7. Add a new bean representing the array of Books.
    1. Right-click on "com.sun.ws.rest.samples.helloworld" package, select "New", "Java Class..." and enter the Class Name as "BookList".
    2. Replace the template class with the following code:

      package com.sun.ws.rest.samples.helloworld;

      /\*\*
       \* @author Arun Gupta
       \*/
      @javax.xml.bind.annotation.XmlRootElement
      public class BookList {
        @javax.xml.bind.annotation.XmlElement
        protected java.util.List<Books> book;

        public BookList() {
          if (book == null)
            book = new java.util.ArrayList<Books>();
        }

        public void add(Books name) {
          book.add(name);
        }

        public java.util.List<Books> getValue() {
          return book;
        }
      }
  8. Add a resource that generates the XML and JSON representation to be consumed a jMaki-wrapped DataTable widget.
    1. Expand Source Packages, right-click on "com.sun.ws.rest.samples.helloworld.resources", select "New", "Java Class..." and enter the Class Name as "TableResource".
    2. Replace the generated template class with the following code:

      package com.sun.ws.rest.samples.helloworld.resources;

      import com.sun.ws.rest.samples.helloworld.BookList;
      import com.sun.ws.rest.samples.helloworld.Books;
      import java.util.List;
      import javax.persistence.EntityManager;
      import javax.persistence.EntityManagerFactory;
      import javax.persistence.Persistence;
      import javax.ws.rs.HttpMethod;
      import javax.ws.rs.ProduceMime;
      import javax.ws.rs.UriTemplate;
      import javax.xml.bind.JAXBException;
      import org.codehaus.jettison.json.JSONArray;
      import org.codehaus.jettison.json.JSONException;
      import org.codehaus.jettison.json.JSONObject;

      /\*\*
       \* @author Arun Gupta
       \*/
      @UriTemplate("/table")
      public class TableResource {
        @HttpMethod
        @ProduceMime("application/xml")
        @UriTemplate("/xml")
        public BookList getXML() throws JAXBException {
          BookList booklist = new BookList();
          for (Books b : getBooks()) {
            booklist.add(b);
          }
          return booklist;
        }

        @HttpMethod
        @ProduceMime("application/json")
        @UriTemplate("/json")
        public JSONObject getJSON() throws JSONException {
          JSONObject tableDataModel = new JSONObject();
          String[] labels = { "Title", "Author", "ISBN", "Description"};
          JSONArray columns = new JSONArray();
          for (String l : labels) {
            JSONObject item = new JSONObject();
            item.put("label", l).put("id", l);
            columns.put(item);
          }
          tableDataModel.put("columns", columns);

          JSONArray rows = new JSONArray();
          for (Books b : getBooks()) {
          JSONObject item = new JSONObject();
            item.put(labels[0], b.getTitle());
            item.put(labels[1], b.getAuthor());
            item.put(labels[2], b.getIsbn());
            item.put(labels[3], b.getDescription());
            rows.put(item);
          }
          tableDataModel.put("rows", rows);

          return tableDataModel;
        }

        private List<Books> getBooks() {
          EntityManagerFactory emf = Persistence.createEntityManagerFactory("HelloWorldPU");
          EntityManager em = emf.createEntityManager();
          List<Books> list = em.createNamedQuery("Books.findAll").getResultList();
          return list;
        }
      }


      The getXML() method returns only the table rows data in an XML format. This XML representation of the resource is then processed by a stylesheet (specified later in the jMaki application) and converted into the JSON format as expected by the jMaki widget. The stylesheet adds the column information as well. The getJSON() method uses low-level JSON APIs to return the data exactly as expected by the jMaki widget (including the column information).

      In the case of returning an XML representation we can return the column information as well but that would only complicate the code for this sample purpose.
  9. Right-click on "com.sun.ws.rest.samples.helloworld", edit "Main.java" and change line 42 to use "TableResource" class instead of "HelloWorldResource". The updated code looks like:

    HttpHandler handler = ContainerFactory.createContainer(
            HttpHandler.class,
            TableResource.class);


    Fix the import.
  10. Right-click on project, select "Properties", choose "Libraries" and add the following JARs by clicking on "Add JAR/Folder" button:
    1. "GLASSFISH_HOME/javadb/lib/derbyclient.jar"
    2. "GLASSFISH_HOME/lib/toplink-essentials.jar"
    3. "GLASSFISH_HOME/lib/toplink-essentials-agent.jar"
  11. Right-click on the Project and select "Run".

This concludes developing/configuring/running the Jersey endpoint. Now let's create a new web application and add a jMaki widget that consumes the resource representation exposed by this endpoint:

  1. In the NetBeans IDE, create a new Web application project. Make sure to install jMaki plugin in the NetBeans IDE. Enable "jMaki Ajax Framework" for the project.
  2. Expand "Web pages", "resources", and edit "xhp.json" to add the following entries at the end:

    ,
    {"id": "jersey-json",
     "url":"http://localhost:9998/table/json"
    },
    {"id": "jersey-xml",
     "url":"http://localhost:9998/table/xml",
     "xslStyleSheet": "table.xsl"
    }


    This defines two external services (Jersey endpoints) that can be used by the jMaki widgets. The first entry is a Jersey endpoint that returns JSON representation of the resource. The second entry is a Jersey endpoint that returns the XML representation and additionally specifies a stylesheet that converts the data from XML format to the JSON format expected by the jMaki widget.
  3. Expand "Web pages", "resources", "xsl" and add a new stylesheet (table.xsl). The content of the stylesheet are given below:

    <?xml version="1.0" encoding="UTF-8" ?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="utf-8"/>

      <xsl:template match="/">
        <xsl:apply-templates select="bookList"/>
      </xsl:template>

      <xsl:template match="bookList">
        {"columns":
          [{"label":"Title","id":"Title"},
           {"label":"Author","id":"Author"},
           {"label":"ISBN","id":"ISBN"},
           {"label":"Description","id":"Description"}],
         "rows": [
        <xsl:apply-templates select="book" />
           ]
         }
      </xsl:template>

      <xsl:template match="book">
        {
          "Title" : "<xsl:value-of select="title" />",
          "Author" : "<xsl:value-of select="author" />",
          "ISBN" : "<xsl:value-of select="isbn" />",
          "Description" : "<xsl:value-of select="description" />"
        }
        <xsl:if test="(position()!=last())">,</xsl:if>
      </xsl:template>
    </xsl:stylesheet>
  4. Drag-and-drop two jMaki-wrapped Yahoo DataTable widget in the main section of the default generated index.jsp.
  5. Change the generated code fragment to use the values from the different Jersey endpoints instead of using the static values. The updated code fragment will look like:

    <a:widget name="yahoo.dataTable"
      service="/xhp?id=jersey-json" />
    <a:widget name="yahoo.dataTable"
      service="/xhp?id=jersey-xml" />
  6. Deploy the project and now the jMaki-wrapped widgets are now displayed in the browser window as shown below. The Firebug dump shows the data received by jMaki.


 

Please leave suggestions on other TOTD that you'd like to see. A complete archive is available here.

Technorati: totd jersey jmaki json netbeans glassfish

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

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