Miles to go ...

Arun Gupta is a Technology Evangelist for Web Services and Web 2.0 Apps at Sun. He was the spec lead for APIs in the Java platform, committer in multiple Open Source projects, participated in standard bodies and contributed to Java EE and SE releases.
« Europe Summer 2007... | Main | Screencast #WS7:... »

http://blogs.sun.com/arungupta/date/20071002 Tuesday October 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

del.icio.us | furl | simpy | slashdot | technorati | digg
Comments:

Hi,
As is usual very good tutorial. Well-explained and clean.
Thank's for your help.

Posted by Freetrax on October 03, 2007 at 12:31 AM PDT #

I'll need a search with grabs the data from by Database maybe with the autocompleter.
Have you a sample for this problem??

Posted by Anja on October 30, 2007 at 12:05 PM PDT #

I am getting:

javax.persistence.PersistenceException: No Persistence provider for EntityManager named HelloWorldPU
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:89)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:60)
at resources.TableResource.getBooks(TableResource.java:63)

I checked that persistence.xml exists, in the correct places, and db connection is fine.

I think this has to do with a deployment issue, but I could not figure out on my own.

One thing that is not clear is on the article in #3

Expand "Web pages", "resources", "xsl" and add a new stylesheet.

what is the name of the stylesheet? Is not mentioned in the article.

Best Regards,
-C.B.

Posted by cambazz@gmail.com on November 28, 2007 at 08:14 PM PST #

I updated the blog with the stylesheet name. Did you check the PU name in persistence.xml is the same as used in the JPA code ?

Posted by Arun Gupta on December 13, 2007 at 11:45 PM PST #

[Trackback] &quot;sakila&quot; is the sample database shipped with MySQL (pronounced as my ess-kew-ell). In the context of Sun Microsystems announcing the agreement to acquire MySQL, I'd like to dedicate this entry to show how this sample database can be exposed a...

Posted by Arun Gupta's Blog on January 24, 2008 at 04:46 AM PST #

Post a Comment:
  • HTML Syntax: NOT allowed
« Europe Summer 2007... | Main | Screencast #WS7:... »

Valid HTML! Valid CSS!

This is a personal weblog, I do not speak for my employer.

--> ajax ajaxworld conf eclipse fitness gem glassfish glassfishday hyderabad india indigo interoperability javaone javaone2008 jax-ws jmaki jpa jruby mac marathon metro microsoft mysql netbeans phobos photography presos railsconf ruby rubyonrails running runninglog runsfm screencast siliconvalleymarathon sun suntechdays swdp tango theserverside totd training traveltips v3 vista wcf web2.0 webservices webtier windows wsaddressing wsit
Locations of visitors to this page

calendar

« July 2008
SunMonTueWedThuFriSat
  
5
6
12
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today

Stats

Today's Page Hits: 18671


Total # blog entries: 642
Total # comments: 1862

www.flickr.com
This is a Flickr badge showing public photos from ArunGupta. Make your own badge here.
Add to Technorati Favorites

Last 50