Arun Gupta, Miles to go ...

Arun Gupta is a technology enthusiast, a passionate runner, and a community guy who works for Sun Microsystems.

http://blogs.sun.com/arungupta/date/20090819 Wednesday August 19, 2009

TOTD #96: GlassFish v3 REST Interface to Monitoring and Management - JSON, XML, and HTML representations


GlassFish Monitoring allows you to monitor the state of various runtime components of the application server. This information is used to identify performance bottlenecks and tuning the system for optimal performance, to aid capacity planning, to predict failures, to do root cause analysis in case of failures and sometimes to just ensure that everything is functioning as expected.

GlassFish Management allows you to manage the running Application Server instance such as query/create/delete resources (JDBC, JMS, etc), stop/restart the instance, rotate the log and other similar functions.

GlassFish v3 exposes Monitoring and Management data using a REST Interface. This Tip Of The Day (TOTD) shows how to play with this new functionality. Rajeshwar's blog has lot of useful information on this topic.

Most of the functionality available in web-based Admin Console and CLI (asadmin) is now available using the REST interface. Both of these are pre-built tools that ships with the GlassFish bundle. The REST interface is a lower level API that enables toolkit developers and IT administrators to write their custom scripts/clients using language of their choice such as Java, JavaScript, Ruby or Groovy.

The default URL for the REST interface of monitoring is "http://localhost:4848/monitoring/domain" and for the management is "http://localhost:4848/management/domain". Each URL provides an XML, JSON and HTML representation of the resources. If a web browser is used then a HTML representation is returned and displayed nicely in the browser. Rajeshwar's blog described a Java client written using Jersey Client APIs that can be used to make all the GET/PUT/POST/DELETE requests. This blog will use something more basic, and extremely popular, to make all the RESTful invocations - cURL.

At this time the monitoring resources are read-only (GET) and management can be done using GET/POST/DELETE methods. POST is used for creating and updating resources/objects and the updates can be partial.
 
Lets get started.

  1. Download the latest continuous build from the trunk and unzip. This functionality is also available in the Web profile bundle. This blog is using build #2023.
  2. Start the application server as:

    ~/tools/glassfish/v3/2023/glassfishv3 >./bin/asadmin start-domain --verbose

    Aug 19, 2009 9:52:45 AM com.sun.enterprise.admin.launcher.GFLauncherLogger info
    INFO: JVM invocation command line:
    /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java
    -cp

    . . .

    INFO: felix.fileinstall.dir            /Users/arungupta/tools/glassfish/v3/2023/glassfishv3/glassfish/domains/domain1/autodeploy-bundles
    Aug 19, 2009 9:53:05 AM 
    INFO: felix.fileinstall.debug          1
    Aug 19, 2009 9:53:05 AM 
    INFO: felix.fileinstall.bundles.new.start          true

  3. Monitoring information - Lets monitor this GlassFish instance using the REST interface.
    1. Retrieve JSON information - As mentioned above, the monitoring resources are read-only and so the information can be accessed as:

      ~/tools/glassfish/v3/2023/glassfishv3 >curl -H "Accept: application/json" http://localhost:4848/monitoring/domain -v
      * About to connect() to localhost port 4848 (#0)
      *   Trying ::1... connected
      * Connected to localhost (::1) port 4848 (#0)
      > GET /monitoring/domain HTTP/1.1
      > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
      > Host: localhost:4848
      > Accept: application/json
      >
      < HTTP/1.1 200 OK
      < Content-Type: application/json
      < Transfer-Encoding: chunked
      < Date: Wed, 19 Aug 2009 17:40:29 GMT
      <
      {Domain:{},"child-resources":["http://localhost:4848/monitoring/domain/server"]}
      * Connection #0 to host localhost left intact
      * Closing connection #0

      The command explicitly asks for JSON representation of the resources. The outbound headers are prepended with ">" and inbound headers with "<". And the JSON representation is shown in the last line as:

      {Domain:{},"child-resources":["http://localhost:4848/monitoring/domain/server"]}

      The key element to remember here is "http://localhost:4848/monitoring/domain/server" which can be used to retrieve more monitoring information.
    2. XML represetation: Lets change the command to ask for XML representation as:

      ~/tools/glassfish/v3/2023/glassfishv3 >curl -H "Accept: application/xml" http://localhost:4848/monitoring/domain -v
      * About to connect() to localhost port 4848 (#0)
      *   Trying ::1... connected
      * Connected to localhost (::1) port 4848 (#0)
      > GET /monitoring/domain HTTP/1.1
      > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
      > Host: localhost:4848
      > Accept: application/xml
      >
      < HTTP/1.1 200 OK
      < Content-Type: application/xml
      < Transfer-Encoding: chunked
      < Date: Wed, 19 Aug 2009 17:43:51 GMT
      <
      <Domain>
          <child-resource>http://localhost:4848/monitoring/domain/server</child-resource>
      </Domain>
      * Connection #0 to host localhost left intact
      * Closing connection #0

      The command changes the "Accept" header to "application/xml" and now the XML representation of the monitoring resources is returned as:

      <Domain>
          <child-resource>http://localhost:4848/monitoring/domain/server</child-resource>
      </Domain>

    3. HTML representation: The command can be altered to get the HTML representation as "curl -H "Accept: text/html" http://localhost:4848/monitoring/domain -v". But HTML is more pleasant when rendered by a browser and so viewing the page "http://localhost:4848/monitoring/domain" in the browser is shown as:

    4. Get more information: As mentioned above, more information about this GlassFish instance can be accessed by GETing from "http://localhost:4848/monitoring/domain/server" and here is the result:

      </tools/glassfish/v3/2023/glassfishv3 >curl -H "Accept: application/json" http://localhost:4848/monitoring/domain/server -v
      * About to connect() to localhost port 4848 (#0)
      *   Trying ::1... connected
      * Connected to localhost (::1) port 4848 (#0)
      > GET /monitoring/domain/server HTTP/1.1
      > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
      > Host: localhost:4848
      > Accept: application/json
      >
      < HTTP/1.1 200 OK
      < Content-Type: application/json
      < Transfer-Encoding: chunked
      < Date: Wed, 19 Aug 2009 17:56:41 GMT
      <
      {Server:{},"child-resources":["http://localhost:4848/monitoring/domain/server/webintegration",
      "http://localhost:4848/monitoring/domain/server/transaction-service",
      "http://localhost:4848/monitoring/domain/server/network",
      "http://localhost:4848/monitoring/domain/server/jvm",
      "http://localhost:4848/monitoring/domain/server/web",
      "http://localhost:4848/monitoring/domain/server/realm",
      "http://localhost:4848/monitoring/domain/server/http-service"]}
      * Connection #0 to host localhost left intact
      * Closing connection #0

      An HTML rendering of this representation looks like:



      You can keep clicking on the links and more detailed information about that resource is displayed. This is just one HTML representation and is purposely kept light-weight. You can certainly grab the XML representation and apply an XSLT to generate your own HTML rendering.

      The monitoring levels for different modules can be easily changed using the management REST interface as explained below.
  4. Management of the GlassFish instance
    1. Lets see all the options supported by management REST interface as:

      ~/tools/glassfish/v3/2023/glassfishv3 >curl -X OPTIONS http://localhost:4848/management/domain -v
      * About to connect() to localhost port 4848 (#0)
      *   Trying ::1... connected
      * Connected to localhost (::1) port 4848 (#0)
      > OPTIONS /management/domain HTTP/1.1
      > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
      > Host: localhost:4848
      > Accept: */*
      >
      < HTTP/1.1 200 OK
      < Content-Type: application/json
      < Transfer-Encoding: chunked
      < Date: Wed, 19 Aug 2009 18:07:14 GMT
      <
      {
        "Method":"GET"


        "Method":"PUT"
      }
      * Connection #0 to host localhost left intact
      * Closing connection #0

      Specifying "-X OPTIONS" switch displays the various HTTP methods supported by the REST interface. Even though the results show GET and PUT, but it really means GET and POST (issue #9177). Lets try "GET" first. 
    2. GET JSON information as:

      ~/tools/glassfish/v3/2023/glassfishv3 >curl -H "Accept: application/json" http://localhost:4848/management/domain -v
      * About to connect() to localhost port 4848 (#0)
      *   Trying ::1... connected
      * Connected to localhost (::1) port 4848 (#0)
      > GET /management/domain HTTP/1.1
      > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
      > Host: localhost:4848
      > Accept: application/json
      >
      < HTTP/1.1 200 OK
      < Content-Type: application/json
      < Transfer-Encoding: chunked
      < Date: Wed, 19 Aug 2009 18:14:46 GMT
      <
      {Domain:{"log-root" : "${com.sun.aas.instanceRoot}/logs","application-root" : "${com.sun.aas.instanceRoot}/applications","locale" : "","version" : "re-continuous"},"child-resources":["http://localhost:4848/management/domain/configs",
      "http://localhost:4848/management/domain/resources","http://localhost:4848/management/domain/servers",
      "http://localhost:4848/management/domain/property","http://localhost:4848/management/domain/applications",
      "http://localhost:4848/management/domain/system-applications","http://localhost:4848/management/domain/stop",
      "http://localhost:4848/management/domain/restart","http://localhost:4848/management/domain/uptime",
      "http://localhost:4848/management/domain/version","http://localhost:4848/management/domain/rotate-log",
      "http://localhost:4848/management/domain/host-port"]}
      * Connection #0 to host localhost left intact
      * Closing connection #0

      As the result shows, there are several RESTful URLs available (in "child-resources" element) to manage this GlassFish instance. For example:
      1. Show the host/port of GlassFish instance as:

        curl -H "Accept: application/json" http://localhost:4848/management/domain/host-port -v

        will show the result as:

        {"GetHostAndPort":{"value" : "dhcp-usca14-132-79.SFBay.Sun.COM:8080"}}

      2. Show that web-based Admin Console is pre-installed as system application using:

        curl -H "Accept: application/json" http://localhost:4848/management/domain/system-applications/application/__admingui -v

        will show the result as:

        {__admingui:{"libraries" : "","availability-enabled" : "false","enabled" : "true","context-root" : "","location" : "${com.sun.aas.installRootURI}/lib/install/applications/__admingui","description" : "","name" : "__admingui","directory-deployed" : "true","object-type" : "system-admin"},"child-resources":["http://localhost:4848/management/domain/system-applications/application/__admingui/module"]}

      3. Query the monitoring levels of different modules as:

        curl -H "Accept: application/json" http://localhost:4848/management/domain/configs/config/server-config/monitoring-service/module-monitoring-levels -v

        to see the result as:

        {ModuleMonitoringLevels:{"transaction-service" : "OFF","ejb-container" : "OFF","jdbc-connection-pool" : "OFF","orb" : "OFF","http-service" : "OFF","connector-connection-pool" : "OFF","jms-service" : "OFF","connector-service" : "OFF","jvm" : "OFF","thread-pool" : "OFF","web-container" : "OFF"},"child-resources":[]}

        And then change the monitoring level of Web container as:

        ~/tools/glassfish/v3/2023/glassfishv3 >curl -X POST -d "web-container=ON" -H "Accept: application/json" http://localhost:4848/management/domain/configs/config/server-config/monitoring-service/module-monitoring-levels -v
        * About to connect() to localhost port 4848 (#0)
        *   Trying ::1... connected
        * Connected to localhost (::1) port 4848 (#0)
        > POST /management/domain/configs/config/server-config/monitoring-service/module-monitoring-levels HTTP/1.1
        > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
        > Host: localhost:4848
        > Accept: application/json
        > Content-Length: 16
        > Content-Type: application/x-www-form-urlencoded
        >
        < HTTP/1.1 200 OK
        < Content-Type: application/json
        < Transfer-Encoding: chunked
        < Date: Wed, 19 Aug 2009 22:01:31 GMT
        <
        * Connection #0 to host localhost left intact
        * Closing connection #0
        "http://localhost:4848/management/domain/configs/config/server-config/monitoring-service/module-monitoring-levels" updated successfully

        The last line shows that the monitoring level is successfull updated and can be verified again as:

        ~/tools/glassfish/v3/2023/glassfishv3 >curl -H "Accept: application/json" http://localhost:4848/management/domain/configs/config/server-config/monitoring-service/module-monitoring-levels -v
        * About to connect() to localhost port 4848 (#0)
        *   Trying ::1... connected
        * Connected to localhost (::1) port 4848 (#0)
        > GET /management/domain/configs/config/server-config/monitoring-service/module-monitoring-levels HTTP/1.1
        > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
        > Host: localhost:4848
        > Accept: application/json
        >
        < HTTP/1.1 200 OK
        < Content-Type: application/json
        < Transfer-Encoding: chunked
        < Date: Wed, 19 Aug 2009 22:36:47 GMT
        <
        * Connection #0 to host localhost left intact
        * Closing connection #0
        {ModuleMonitoringLevels:{"transaction-service" : "OFF","ejb-container" : "OFF","jdbc-connection-pool" : "OFF","orb" : "OFF","http-service" : "OFF","connector-connection-pool" : "OFF","jms-service" : "OFF","connector-service" : "OFF","jvm" : "OFF","thread-pool" : "OFF","web-container" : "ON"},"child-resources":[]}
      4. Stop this GlassFish instance using:

        curl -H "Accept: application/json" http://localhost:4848/management/domain/stop -v

        Or restart the instance using:

        curl -H "Accept: application/json" http://localhost:4848/management/domain/restart -v
      5. Create a JDBC resource using an existing connection pool
        1. Lets see all the resources that are available:

          curl -H "Accept: application/json" http://localhost:4848/management/domain/resources -v

          and the results are shown as:

          {Resources:{},"child-resources":["http://localhost:4848/management/domain/resources/jdbc-connection-pool",
          "http://localhost:4848/management/domain/resources/jdbc-resource"]}

        2. View all the JDBC connection pools as:

          curl -H "Accept: application/json" http://localhost:4848/management/domain/resources/jdbc/connection-pool -v

          and the results are shown as:

          {JdbcConnectionPool:{},"child-resources":["http://localhost:4848/management/domain/resources/jdbc-connection-pool/__TimerPool",
          "http://localhost:4848/management/domain/resources/jdbc-connection-pool/DerbyPool"]}

        3. See all the JDBC resources available as:

          curl "Accept: application/json" http://localhost:4848/management/domain/resources/jdbc-resource -v

          and the results are shown as:

          {JdbcResource:{},"child-resources":["http://localhost:4848/management/domain/resources/jdbc-resource/jdbc/__TimerPool",
          "http://localhost:4848/management/domain/resources/jdbc-resource/jdbc/__default"]}

        4. See all the OPTIONS accepted for JDBC resource creation as:

          curl -X OPTIONS -H "Accept: application/json" http://localhost:4848/management/domain/resources/jdbc-resource -v

          with the result as:

          {
            "Method":"POST",
            "Message Parameters":{
              "id":{"Acceptable Values":"","Default Value":"","Type":"class java.lang.String","Optional":"false"},
              "enabled":{"Acceptable Values":"","Default Value":"true","Type":"class java.lang.Boolean","Optional":"true"},
              "description":{"Acceptable Values":"","Default Value":"","Type":"class java.lang.String","Optional":"true"},
              "target":{"Acceptable Values":"","Default Value":"","Type":"class java.lang.String","Optional":"true"},
              "property":{"Acceptable Values":"","Default Value":"","Type":"class java.util.Properties","Optional":"true"},
              "connectionpoolid":{"Acceptable Values":"","Default Value":"","Type":"class java.lang.String","Optional":"false"}
            }


            "Method":"GET"

        5. Finally, create the JDBC resource as:

          ~/tools/glassfish/v3/2023/glassfishv3 >curl -d "id=jdbc/sample&connectionpoolid=DerbyPool" http://localhost:4848/management/domain/resources/jdbc-resource -v
          * About to connect() to localhost port 4848 (#0)
          *   Trying ::1... connected
          * Connected to localhost (::1) port 4848 (#0)
          > POST /management/domain/resources/jdbc-resource HTTP/1.1
          > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
          > Host: localhost:4848
          > Accept: */*
          > Content-Length: 42
          > Content-Type: application/x-www-form-urlencoded
          >
          < HTTP/1.1 201 Created
          < Content-Type: text/html
          < Transfer-Encoding: chunked
          < Date: Wed, 19 Aug 2009 20:45:51 GMT
          <
          * Connection #0 to host localhost left intact
          * Closing connection #0
          "http://localhost:4848/management/domain/resources/jdbc-resource/jdbc/sample" created successfully.

          Note, this is a POST request. The JDBC resource name and JDBC connection pool id are passed as CLI parameters using "-d" switch. The last line shows that the JDBC resource was created successfully.
        6. And finally query the JDBC Resources again as:

          curl -H "Accept: application/json" http://localhost:4848/management/domain/resources/jdbc-resource -v

          to see the updated result as:

          {JdbcResource:{},"child-resources":["http://localhost:4848/management/domain/resources/jdbc-resource/jdbc/__TimerPool",
          "http://localhost:4848/management/domain/resources/jdbc-resource/jdbc/__default",
          "http://localhost:4848/management/domain/resources/jdbc-resource/jdbc/sample"]}

        Similarly JDBC connection pools can be created.
    3. POST can be used to update the top-level attributes such as "log-root" and "application-root". The name of these attributes are shown in the result of GET.
    4. As earlier, XML representation of management resources can be obtained as:

      ~/tools/glassfish/v3/2023/glassfishv3 >curl -H "Accept: application/xml" http://localhost:4848/management/domain -v
      * About to connect() to localhost port 4848 (#0)
      *   Trying ::1... connected
      * Connected to localhost (::1) port 4848 (#0)
      > GET /management/domain HTTP/1.1
      > User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
      > Host: localhost:4848
      > Accept: application/xml
      >
      < HTTP/1.1 200 OK
      < Content-Type: application/xml
      < Transfer-Encoding: chunked
      < Date: Wed, 19 Aug 2009 18:17:07 GMT
      <
      <Domain log-root="${com.sun.aas.instanceRoot}/logs" application-root="${com.sun.aas.instanceRoot}/applications" locale="" version="re-continuous">
          <child-resource>http://localhost:4848/management/domain/configs</child-resource>
          <child-resource>http://localhost:4848/management/domain/resources</child-resource>
          <child-resource>http://localhost:4848/management/domain/servers</child-resource>
          <child-resource>http://localhost:4848/management/domain/property</child-resource>
          <child-resource>http://localhost:4848/management/domain/applications</child-resource>
          <child-resource>http://localhost:4848/management/domain/system-applications</child-resource>
          <child-resource>http://localhost:4848/management/domain/stop</child-resource>
          <child-resource>http://localhost:4848/management/domain/restart</child-resource>
          <child-resource>http://localhost:4848/management/domain/uptime</child-resource>
          <child-resource>http://localhost:4848/management/domain/version</child-resource>
          <child-resource>http://localhost:4848/management/domain/rotate-log</child-resource>
          <child-resource>http://localhost:4848/management/domain/host-port</child-resource>
      * Connection #0 to host localhost left intact
      * Closing connection #0

      Just changing the "Accept" header to "application/xml" did the trick.
    5. And an HTML representation can be obtained by viewing the URL "http://localhost:4848/management/domain" in the browser with result as:



Just like GlassFish v3, the REST interface is extensible as well. So if a new container is plugged in that generates data (possibly through probes) captured in the runtime tree, that is automatically exposed in the RESTful interface.

Now for the Mac users, Safari prefers XML over HTML. Basically a resource, that can be served using both XML and HTML representation (as our Management and Monitoring interface), is served as XML by Safari and HTML by Firefox. So use Firefox on Mac if you want HTML rendering.

How will you use GlassFish REST interface ?

Do your application server provide that level of administration capability ?

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

Technorati: totd glassfish v3 rest management monitoring jersey

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

http://blogs.sun.com/arungupta/date/20081204 Thursday December 04, 2008

JavaFX 1.0 launched - access services hosted on embedded GlassFish


Today Sun announces the availability of Java FX 1.0.

JavaFX 1.0 is a rich client platform for creating and delivering Rich Internet Applications across all screens (desktop, browser, and mobile) of your life. It consists of the following key components:


  • JavaFX SDK includes JavaFX script compiler and runtime tools, and a host of libraries to create RIAs for desktop, browser and mobile platforms, command-line tools & Ant tasks and other goodies.
  • NetBeans 6.5 support (as plugin or bundled with IDE) that allows to build, preview and debug JavaFX applications using NetBeans IDE. If you prefer CLI support then SDK can be downloaded.
  • Production Suite is a suite of tools and plugins for creative tools (such as Illustrator CS3+) that allows graphical assets to be exported to JavaFX applications.
The beauty of JavaFX is that its fully integrated with the Java Runtime and takes advantage of the performance and ubiquity of Sun's Java Runtime Environment that is installed on literally billions of devices worldwide. Hence, JavaFX applications will run on any desktop, browser, mobile device or any other connected device that runs the Java Runtime Environment.

This blog shows how to create a simple JavaFX application using NetBeans IDE. The application plays a movie, allows the viewer to cast a vote if they liked it, and see aggregate response from other viewers. The application is developed using NetBeans 6.5, JavaFX 1.0 plugin, and coded using JavaFX Script. The voting engine is deployed as a RESTful Web service using Jersey on GlassFish.

In terms of user experience, running the NetBeans project shows a window playing the movie. The first mouse hover over the window allows the viewer to click on "I love it" or "Not so great" and cast their vote as shown below:



Any subsequent mouse hover shows aggregated results from other viewers as shown below:



The results are not interesting if there is a single viewer of the movie. But for a production environment, this movie will be played by multiple users concurrently and the percentage numbers will be more meaningful. You can close the window and run the project again to vote again, as many times as you like :)

For those who like to see quick results, here is a 4-step guide to get started:
  1. In NetBeans 6.5 IDE, install JavaFX plugin as explained here and RESTful Web services plugin as explained here. Both the plugins may be installed in one step by selecting the required plugins together.
  2. Download NetBeans project for JavaFX client from here and add Jersey dependencies as explained in bullet #5 below. 
  3. Download Web service endpoint Maven project from here and deploy the endpoint as explained in bullet #4 below.
  4. Run the JavaFX application as explained here.
The remainder of this blog explains the details and shows how to construct the demo from scratch.

Lets first create the JavaFX application that plays the video movie.
  1. In NetBeans 6.5, install "JavaFX SDK" plugin.  In the "Tools" menu, "Plugins", search on "JavaFX", select "JavaFX SDK" and click on "Install".
  2. Create a new project of type "JavaFX", "JavaFX Script Application". Take the default values as shown below:



    and click on "Finish".
  3. The source code for this class can be downloaded from here or alternatively constructed as explained in the sub-bullets.
    1. In the newly created class, change the Stage (root area for all scene content) to:

      Stage {
         title: "GlassFish Media Player"
         width: 625
         height: 360
         resizable: false
         scene: myScene
      }
    2. Create a scene that contains the view of the media to be played and controls the display of the Vote or Result nodes:

      var myScene: Scene = Scene {
         content: MediaView {
                 fitWidth: 625
                 fitHeight: 360
                 mediaPlayer: bind myPlayer

                 onMouseEntered: function( e: MouseEvent ):Void {
                     println("mouse entered");
                     if (voted == false) {
                         insert Vote{} into myScene.content;
                     } else {
                         insert Result{} into myScene.content;
                     }
                 }

                 onMouseExited: function( e: MouseEvent ):Void {
                     delete myScene.content[1]
                 }

             }
      }
    3. Create a Media Player to use with the scene:

      var myPlayer: MediaPlayer = MediaPlayer{
          autoPlay: true
          media: bind myMedia
      };
    4. Create the media object to be used with the Media Player:

      var myMedia: Media = Media {
          source: "http://sun.edgeboss.net/download/sun/media/1460825906/1460825906_2957290001_DayEarth-Bluray.flv"
         };

      You can change the location of the movie here in the media player. For example, changing it to "http://mediacast.sun.com/users/ArunGupta/media/v3prelude-nb65-webapp.flv" will start playing the screencast #27.
    5. Create a Vote class that is a CustomNode and appears when a user's mouse enters the scene where the video is playing. The user can select whether he likes the clip or not and the vote is recorded making a Web service call using Jersey Client APIs:

      class Vote extends CustomNode {
         override function create():Node {
             return Group {
                 content: [
                     Rectangle {
                         fill: Color.GREEN
                         x: 185
                         y: 145
                         width: 243
                         height: 38
                         arcWidth: 20
                         arcHeight: 20
                     },

                     Text {
                         x: 195
                         y: 170
                         fill: Color.WHITE
                         font: Font {
                             size: 18
                         }
                         content: "I love it"
                     },

                     Rectangle{
                         x: 191
                         y: 148
                         smooth: false
                         width: 73
                         height: 32
                         fill: Color.TRANSPARENT

                         onMouseClicked: function( e: MouseEvent ):Void {
                             println("clicked I love it");
                             voted = true;
                             wsClient.voteLoveIt();
                             delete myScene.content[1]
                         }
                     },

                     Text{
                         x: 305
                         y: 170
                         fill: Color.WHITE
                         font: Font {
                             size: 18
                         }
                         content: "Not so great"
                         },

                     Rectangle {
                         x: 301
                         y: 148
                         smooth: false
                         width: 118
                         height: 32
                         fill: Color.TRANSPARENT
                        
                         onMouseClicked: function( e: MouseEvent ):Void {
                             voted = true;
                             println("clicked Not so great");
                             wsClient.voteNotSoGreat();
                             delete myScene.content[1]
                         }
                     }
                 ]
             }
         }
      };
    6. Create a Result class that is a CustomNode and simply reports on how many voters like this clip:

      class Result extends CustomNode {
         override function create():Node {
             var resultPercent = wsClient.showResults();
             var resultString = "{resultPercent} voters liked this clip";

             return Group {
                 content: [
                     Rectangle {
                         fill: Color.BLUE
                         x: 187
                         y: 145
                         width: 244
                         height: 38
                         arcWidth: 20
                         arcHeight: 20

                         onMouseClicked: function( e: MouseEvent ):Void {
                             delete myScene.content[1]
                         }
                     },

                     Text {
                         x: 199
                         y: 170
                         fill: Color.WHITE
                         font: Font {
                             size: 18
                         }
                         content: resultString
                     }
                 ]
             }
         }
      };
    7. Add two instance variables:

      var voted = false;
      var wsClient = new WebserviceClient;

      The first variable captures if the viewer has already voted and the second variable is an instance to the RESTful Web service client.
    8. Add the following import statements:

      import javafx.scene.*;
      import javafx.scene.input.MouseEvent;
      import javafx.scene.media.Media;
      import javafx.scene.media.MediaPlayer;
      import javafx.scene.media.MediaView;
      import javafx.scene.paint.Color;
      import javafx.scene.shape.Rectangle;
      import javafx.scene.text.Font;
      import javafx.scene.text.Text;
      import javafx.stage.Stage;

      "Fix Imports" should be able to fix them and bug #154307 is already filed for that.
  4. Create a new class that is used to capture the Vote as:

    @javax.xml.bind.annotation.XmlRootElement
    public class VoteBean {
        public static enum VOTE { LOVE_IT, NOT_SO_GREAT };
       
        public VOTE vote;

        public VoteBean() { vote = VOTE.LOVE_IT; }
        public VoteBean(VOTE vote) {
            this.vote = vote;
        }
    }

    This is a simple Javabean with a standard JAXB annotation. This ensures that XML is used as the data format for transfering results between client and endpoint. The source code for this class is available here.
  5. Add Jersey libraries to the project by right-clicking on Project, select Libraries, click on "Add Library...", select "JAX-RS 1.0" and "Jersey 1.0 (JAX-RS RI)", and click on "Add Library".



    If these libraries are not available then install the "RESTful Web Services" plugin from the Plugin Center.
  6. And finally add the class that invokes the RESTful Webservice endpoint:

    public class WebserviceClient {

        private static com.sun.jersey.api.client.WebResource createWebResource() {
            return com.sun.jersey.api.client.Client.create().
                    resource("http://localhost:8080/movie-feedback-webapp/webresources/myresource");
        }

        public static void voteLoveIt() {
            createWebResource().type("application/json").
                    post(new VoteBean(VoteBean.VOTE.LOVE_IT));
        }

        public static void voteNotSoGreat() {
            createWebResource().type("application/json").
                    post(new VoteBean(VoteBean.VOTE.NOT_SO_GREAT));
        }

        public static String showResults() {
            return createWebResource().get(String.class);
        }
    }

    The Webservice endpoint will be hosted at "http://localhost:8080/movie-feedback-webapp/webresources/myresource". A WebResource is created from the Client. The POST methods are used to cast the user vote and GET method is used to retrieve the aggregated results. The source code for this class is available here.
Now lets create the RESTful endpoint using Jersey and deploy on GlassFish.
  1. Create and deploy a RESTful Web service endpoint
    1. Create a template RESTful Web service endpoint as described in TOTD #56. Lets use the artifactId as "movie-feedback-webapp".
    2. Create the bean "VoteBean" in "org.glassfish.samples" package. This is the exactly same bean used earlier by the client:

      @javax.xml.bind.annotation.XmlRootElement
      public class VoteBean {
          public static enum VOTE { LOVE_IT, NOT_SO_GREAT };
          public VOTE vote;

          public VoteBean() { vote = VOTE.LOVE_IT; }
          public VoteBean(VOTE vote) {
              this.vote = vote;
          }
      }
    3. Update the generated resource
      1. Add @com.sun.jersey.spi.resource.Singleton as class annotation so that only one instance of the resource is created for the entire web application. This allows to save state (preferences from other users) in the RESTful resource.
      2. Add two instance variables:

            int loveIt;
            int noSoGreat;
      3. Add a method that will process HTTP POST requests as:

            @POST
            public void postOneVote(VoteBean bean) {
                if (bean.vote == VoteBean.VOTE.LOVE_IT) {
                    loveIt++;
                } else {
                    noSoGreat++;
                }
                System.out.println("In POST: " + bean.vote);
            }

        This method stores the vote in the resource. The handling of POST request messages by Jersey is explained in TOTD #58.
      4. Add a method that will process HTTP GET requests as:

            @GET
            @Produces("text/plain")
            public String getOpinion() {
                if (loveIt == 0 && noSoGreat == 0)
                    return "No votes cast yet!";
                return (loveIt * 100) / (loveIt + noSoGreat) + "%";
            }

        This method calculates the percentage of viewers who liked the movie.
    4. Deploy the endpoint using "mvn glassfish:run" in "movie-feedback-webapp" directory.
Now run the JavaFX application by right-clicking on the project and selecting "Run Project" and start voting! The percentage results will vary if the movie is voted upon more than once.

This blog showed:
  • How to install JavaFX capabilities to an existing NetBeans 6.5 installation
  • How to create a simple JavaFX application that plays media files
  • Integrate it with existing Java libraries (Jersey client libraries in this case)
  • Invoke services hosted on GlassFish
The steps followed in this blog allows for rapid development/debugging of JavaFX application accessing resources using embeddable GlassFish but are not ideal for production deployments. A future blog will show how this JavaFX application can be deployed as a Java Web Start application and scaled for mulitple users.

The javafx.com/samples has loads of samples and javafx.com/tutorials shows how to build your own applications. The JavaFX Community Wiki is a great place to collaborate.

Send your Jersey questions to users@jersey.dev.java.net, GlassFish questions to GlassFish Forum, and JavaFX questions to JavaFX Forums.

Technorati: glassfish v3 jersey webservices javafx netbeans

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

http://blogs.sun.com/arungupta/date/20081201 Monday December 01, 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

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

http://blogs.sun.com/arungupta/date/20081126 Wednesday November 26, 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

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

http://blogs.sun.com/arungupta/date/20081125 Tuesday November 25, 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

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

http://blogs.sun.com/arungupta/date/20080128 Monday January 28, 2008

GlassFish at WebGuild Web 2.0 Conference & Expo 2008 - Jan 29

Sun Microsystems is a sponsor of WebGuild's Web 2.0 Conference & Expo and you can meet me at Sun booth in the exhibitor hall. Register for a FREE exhibitor pass here.

Ask me about:

  • How GlassFish provides an easy-to-use and production-quality development and deployment environment for your "Web 2.0" applications ?
  • How NetBeans IDE provides a rich extensible platform to create such applications ?
  • What is jMaki and how it allows you to use best tools & libraries to create Rich Internet Applications ?
  • How GlassFish is a "green" alternative for deploying JRuby-on-Rails applications ?
  • How RESTful Web services can be easily generated using Jersey from a database table ?
  • Or anything else you want to know :)

I will be at the WebGuild's Web 2.0 Conference & Expo

Technorati: conf jmaki jruby netbeans glassfish rubyonrails jruby jersey

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

http://blogs.sun.com/arungupta/date/20080124 Thursday January 24, 2008

RESTful representation of "sakila" using GlassFish and NetBeans IDE

"sakila" 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 as a RESTful Web service endpoint and deployed on GlassFish using Jersey Tooling Plugin (0.4.1 with Jersey 0.4) in NetBeans IDE.

Lets get started!

  1. Install MySQL & the sample database "sakila".
    1. Download and Install MySQL Community Server.
    2. Download sakila sample database.
    3. Install the database as described here.
    4. Start MySQL database by giving the command 'mysqld-nt --user root --console' in bin directory on Windows or './bin/mysqld_safe' from MySQL directory on Unix flavors.
  2. Create the Project & Database Connection
    1. In NetBeans IDE, create a new Web project and name it as "sakila". Choose "GlassFish v2" as the "Server:".
    2. In the "Services" tab of NetBeans IDE, expand "Drivers" and add MySQL Connector/J driver if it does not exist already.
    3. Create a new new database connection by right-clicking on "Drivers" and specifying the parameters as shown below:

  3. Create the Persistence Unit
    1. Right-click on the project and select "New", "Entity Classes from Database...". In "Data Source", select "New Data Source..." and specify the values as shown below:

    2. Click on "film" in "Available Tables" and click on "Add >" as shown below:



      Click on "Next >".
    3. Click on "Create Persistence Unit..." and take all the defaults as shown below:



      Click on "Create".
    4. Enter the package name as "sakila" as shown below:



      and click on "Finish".
    5. In the NetBeans project explorer, expand "Configuration Files" and open "persistence.xml". Specify the username and password by replacing <properties/> with the following fragment:

      <properties>
        <property name="toplink.jdbc.user" value="root"/>
        <property name="toplink.jdbc.password" value=""/>
      </properties>


      Make sure to match the username and password to your MySQL installation.
  4. Create RESTful Web service endpoint
    1. In NetBeans IDE, click on "Tools", "Plugins", "Available Plugins", "RESTful Web Services" and then click on "Install". This installs the Jersey Tooling Plugin in the IDE.
    2. Right-click on the project, select "New", "RESTful Web Services from Entity Classes...".
    3. Click on "Add >>", take all other defaults as shown below:



      click on "Next >", take all defaults and then "Finish".
  5. Test RESTful Web Services
    1. Right-click on the project and select "Test RESTful Web Services". The following web page is presented in the browser:

    2. Click on "films" and then on "Test" as shown below:



      Clicking on "Test" button or the URL "http://localhost:8080/sakila/resources/films/" shows the RESTful representation of the "Film" table. The default representation shows 10 records from the table where each entry returns the "id" of the film and a reference to the detailed entry.

      You can view more entries (say 40) by giving the URL "http://localhost:8080/sakila/resources/films/?max=40". Additional fields from the table can be displayed by adding getter methods to "converter.FilmRefConverter" class such as:

      @XmlElement
      public String getTitle() {
        return entity.getTitle();
      }


      to return the film title in addition to the fields already returned. The different columns in the table can be viewed by going to the "Services" tab, expanding the sakila database connection created earlier as shown below:



      The modified output (with film title included) looks as shown below:

Here are few more ideas for you to explore:

  • Create RESTful representations of other tables using the steps described above.
  • Display the data from different tables in a jMaki-wrapped Yahoo or Dojo data table as explained in TOTD #10.
  • Display the data retrieved from the database in a JSP page as described in Hello JPA World.
  • Create a CRUD application using jMaki Data Table as described in TOTD #15 or Screencast #Web10.

A JRuby-on-Rails application using MySQL is explained here. TOTD #9 explains how JDBC connection pooling in GlassFish can be used for a JRuby-on-Rails application using MySQL.

The key message here is MySQL can be very easily used with GlassFish and NetBeans IDE makes it possible! Once MySQL becomes part of Sun, this integration is going to be much more seamless for the betterment of community.

All the entries on this blog using MySQL can be found here. And last but not the least, Welcome aboard MySQL!

A NetBeans project with all the source code can be downloaded from here. You will still need to setup the database connection and need to make sure the correct version of Jersey plug-in as well :)

Technorati: glassfish netbeans jersey mysql sakila jpa jmaki rubyonrails

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

http://blogs.sun.com/arungupta/date/20071231 Monday December 31, 2007

Screencast #Web11: Travel Map - Another Real-life app using jMaki & Jersey

In my role of Technology Evangelist, I get the opportunity to meet a lot of community (folks like you :) all around the world. In the year 2007, I represented GlassFish (and related technologies - Metro, jMaki and Jersey) at multiple conferences. This blog introduces a  new real-life application that plots all the places I visited this year on a jMaki-wrapped Google Map widget. Clicking on the marker shows more information about the event such as dates and the blog entry covering the event.

Play the video below to see how the application looks like.

Here is the architecture of this application:

travel map architecture

It consists of a server-side and a client-side applications - developed as NetBeans projects.

  1. Server-side project - A RESTful Web service endpoint that provides resource represenations for all the events attended and associated meta information such as date and blog URLs. This endpoint is created using Jersey.
  2. Client-side project - A jMaki-enabled Web application that consumes the representations generated by the RESTful Web service and plots the information on a jMaki-wrapped Google Map widget.

Both the server-side and client-side are deployed on GlassFish.

This is only a sample application so optimizations are certainly possible and corner cases (such as no blog entry for a particular visit) are not accounted for. But the application still demonstrates the concept. The fully built application looks like as shown below:

Arun's Travel Map 2007

My first presentation in this role was Sun Tech Days Atlanta (highlighted in the image). This application generates an interactive Google Map so feel free to zoom in/out and click 

And one last thing before we build the application. Here is the list of technologies and associated concepts used to build this application:

  1. Jersey
    1. Shows an example of how RESTful Web services can be easily generated from JPA Entity Classes.
    2. Shows how all the resource representations (instead of reference to individual resources) can be returned by a Jersey endpoint.
  2. jMaki
    1. Shows how to consume XML data from an external service (RESTful Web service endpoint) in this case.
    2. Shows how the underlying data model of a widget (Google Map in this case) can be accessed and manipulated.
  3. GlassFish
    1. All the applications are deployed on GlassFish - implicit in the development/deplyment process through seamless integration with NetBeans.
  4. NetBeans 6
    1. Used for generation of RESTful Web services from JPA Entity Classes.
    2. Used for generating/deploying jMaki projects and drag-and-drop of jMaki-wrapped widgets.
  5. JavaScript Closures - to persist the state for asynchronous callback functions
  6. JavaScript DOM processing - to process the XML data received from Jersey endpoint.
  7. Google Maps API
    1. Generate meaningful markers on each location
    2. Populate Google Map from a RESTful Web service endpoint
  8. Java Persistence API - to retrieve data from the database.

And finally, lets build this application. Lets build the RESTful Web service endpoint project first.

  1. Create and Populate the Database
    1. In the NetBeans IDE, go to Services tab, and connect to the database with URL "jdbc:derby://localhost:1527/sample [app on APP]" (right-click and select "Connect...").
    2. Right-click on this database and select "Execute Command..." and create a table by giving the following command:

      create table EVENTS (id int GENERATED ALWAYS AS IDENTITY,
                          event_name varchar(255),
                          dates varchar(20),
                          venue varchar(255),
                          blogs varchar(2056),
                          PRIMARY KEY (id))


      Notice, the "id" column is marked as IDENTITY that instructs the database to auto generate the values for this column and increment by 1 (default) for each row. This column is also marked as the primary key.
    3. Again right-click on the database and select "Execute Command..." to add data to the table by giving the following command:

      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('SunTech Days - Atlanta', 'Jan 16 - Jan 17', 'Cobb Galleria Center, Two Galleria Parkway, Atlanta, Georgia, 30339', 'http://blogs.sun.com/arungupta/entry/wsit_and_web_2_0');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('jMaki Day', ' Feb 23', '4150 Network Circle Santa Clara, CA 95054', 'http://blogs.sun.com/arungupta/entry/sun_internal_jmaki_day_review');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Ajax World - New York', 'Mar 19 - Mar 21', 'The Roosevelt Hotel, 45 E 45th St, New York, NY 10017', 'http://blogs.sun.com/arungupta/entry/sun_ajax_world');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('The Server Side Java Symposium - Las Vegas', 'Mar 22', '3355 Las Vegas Blvd. South Las Vegas, NV 89109', 'http://blogs.sun.com/arungupta/entry/sun_the_server_side_java, http://blogs.sun.com/arungupta/entry/tango_at_venetian_las_vegas');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('JavaOne - San Francisco', 'May 7 - May 11', 'Moscone Center, 747 Howard Street, San Francisco, CA 94103', 'http://blogs.sun.com/arungupta/entry/slides_for_ts_4865, http://blogs.sun.com/arungupta/entry/javaone_2007_day_1_finished, http://blogs.sun.com/arungupta/entry/javaone_2007_day_1, http://blogs.sun.com/arungupta/entry/javascript_everywhere_javaone_2007_demo, http://blogs.sun.com/arungupta/entry/excel_using_wsit_javaone_2007, http://blogs.sun.com/arungupta/entry/ts_4865_takes_two_to, http://blogs.sun.com/arungupta/entry/communityone_glassfish_day_report, http://blogs.sun.com/arungupta/entry/javaone_2007_backstage, http://blogs.sun.com/arungupta/entry/javaone_2007_is_almost_here, http://blogs.sun.com/arungupta/entry/my_javaone_2007_picks');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Rails Conf - Portland', 'May 17 - May 20', '777 NE MLK, Jr. Blvd. Portland, OR 97232', 'http://blogs.sun.com/arungupta/entry/tim_bray_s_keynote_session, http://blogs.sun.com/arungupta/entry/sun_rails_conf_2007_keep, http://blogs.sun.com/arungupta/entry/getting_started_with_jruby_tutorial, http://blogs.sun.com/arungupta/entry/jmaki_netbeans_and_glassfish_in');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Google Developer Day - San Jose', 'May 31', '150 W San Carlos St San Jose, CA 95113', 'http://blogs.sun.com/arungupta/entry/google_developer_day_report');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Mashup Camp - Mountain View', 'Jul 18 - Jul 19', 'Computer History Museum, 1401 N Shoreline Blvd., Mountain View, CA 94043', 'http://blogs.sun.com/arungupta/entry/jmaki_at_mashup_camp_report, http://blogs.sun.com/arungupta/entry/jmaki_mashup_camp');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('OSCON - Portland', 'Jul 23 - Jul 27', '777 NE MLK, Jr. Blvd. Portland, OR 97232', 'http://blogs.sun.com/arungupta/entry/jmaki_oscon');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('JRuby Hack Day - San Francisco', 'Aug 8', '1201 8th St, San Francisco, CA 94107', 'http://blogs.sun.com/arungupta/entry/jruby_on_rails_hackday_report, http://blogs.sun.com/arungupta/entry/learn_jruby_on_rails_free');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Rich Web Experience - San Jose', 'Sep 6 - Sep 8', '170 S Market St, San Jose, CA 95113', 'http://blogs.sun.com/arungupta/entry/the_rich_web_experience_2007, http://blogs.sun.com/arungupta/entry/jmaki_javafx_the_rich_web');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Rails Conf Europe - Berlin', 'Sep 17 - Sep 19', 'Maritim Pro Arte, Friedrichstrasse 151, 10117 Berlin', 'http://blogs.sun.com/arungupta/entry/rails_conf_europe_2007_day2, http://blogs.sun.com/arungupta/entry/rails_conf_europe_2007_day1, http://blogs.sun.com/arungupta/entry/rails_conf_europe_2007_day, http://blogs.sun.com/arungupta/entry/jmaki_netbeans_and_glassfish_in1');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Sun Tech Days - Rome', 'Sep 24 - Sep 25', 'Meliá Roma Aurelia Antica, Vía Aldobrandeschi, 223  Rome ITALY  00163', 'http://blogs.sun.com/arungupta/entry/netbeans_day_rome_2007, http://blogs.sun.com/arungupta/entry/travel_tips_to_rome, http://blogs.sun.com/arungupta/entry/glassfish_metro_jersey_and_jmaki');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Sun Tech Days - Milan', 'Sep 26 - Sep 28', 'ATA Hotel Quark - Via Lampedusa 11/a 20141 Milano, Italia', 'http://blogs.sun.com/arungupta/entry/glassfish_day_milan_2007');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Mid West Java Tech Days - Minneapolis', 'Oct 16', 'University of St Thomas, MPL 201, 1000 LaSalle Avenue, Minneapolis, MN 55403-2005', 'http://blogs.sun.com/arungupta/entry/mid_west_java_tech_days, http://blogs.sun.com/arungupta/entry/metro_and_jmaki_in_minneapolis');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Mid West Java Tech Days - Chicago', 'Oct 18', 'Donald E Stephens Convention Center, 9301, W Bryn Mawr Ave, Rosemont IL 60018', 'http://blogs.sun.com/arungupta/entry/mid_west_java_tech_days1, http://blogs.sun.com/arungupta/entry/crowne_plaza_chicago_o_hare, http://blogs.sun.com/arungupta/entry/metro_and_jmaki_in_minneapolis');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Silicon Valley Code Camp - Los Altos', 'Oct 27', 'Foothill College, Los Altos, CA', 'http://blogs.sun.com/arungupta/entry/silicon_valley_code_camp_trip, http://blogs.sun.com/arungupta/entry/metro_jmaki_silicon_valley_code');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Sun Tech Days - Beijing', 'Nov 1 - Nov 3', 'Beijing International Convention Center, No.8 Beichendong Road Chaoyang District, Beijing', 'http://blogs.sun.com/arungupta/entry/glassfish_day_beijing_2007_by, http://blogs.sun.com/arungupta/entry/wangfujing_street_authentic_china_in, http://blogs.sun.com/arungupta/entry/sun_tech_days_beijing_talent, http://blogs.sun.com/arungupta/entry/sun_tech_days_beijing_day, http://blogs.sun.com/arungupta/entry/travel_tips_to_beijing, http://blogs.sun.com/arungupta/entry/glassfish_day_beijing');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Toronto', 'Nov 21', 'Toronto City Center', 'http://blogs.sun.com/arungupta/entry/metro_jmaki_jruby_glassfish_q');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Montreal', 'Nov 21', 'Montreal City Center', 'http://blogs.sun.com/arungupta/entry/metro_jmaki_jruby_glassfish_q');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('GlassFish - Delhi University', 'Dec 3', 'New Delhi', 'http://blogs.sun.com/arungupta/entry/glassfish_delhi_university');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('FOSS.IN - Bangalore', 'Dec 4', 'India Institute of Science, Bangalore', 'http://blogs.sun.com/arungupta/entry/packaging_java_apps_for_ubuntu, http://blogs.sun.com/arungupta/entry/foss_in_schedules_now_available, http://blogs.sun.com/arungupta/entry/glassfish_foss_in_2007');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Bangalore', 'Dec 4', 'Bangalore', 'http://blogs.sun.com/arungupta/entry/glassfish_bangalore_chennai_and_pune');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Chennai', 'Dec 5', 'Chennai', 'http://blogs.sun.com/arungupta/entry/glassfish_bangalore_chennai_and_pune');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Pune', 'Dec 6', 'Pune', 'http://blogs.sun.com/arungupta/entry/glassfish_bangalore_chennai_and_pune');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - San Francisco', 'Dec 17', 'San Francisco', 'http://blogs.sun.com/arungupta/');

      These SQL statements populate the database with details about my visits in 2007. If you'd like to develop a similar application highlighting your visits then you'll need to modify the VALUES clause to match accordingly.
  2. Create and Configure RESTful Web service endpoint
    1. Create a Persistence Unit as described in "Generating Entity Classes from Database" section in Getting Started with RESTful Web Services. Lets say the project name is "WebApplication3", package name is "events" and the table name to generate Entity classes is EVENTS. Take everything else as the defaults.
    2. Generate a RESTful Web service as described in "Generating RESTful Web Services from Entity Classes" section in Getting Started with RESTful Web Services.
      1. Add a new class EventsList in the events package as:

        @javax.xml.bind.annotation.XmlRootElement
        public class EventsList {
          @javax.xml.bind.annotation.XmlElement
          protected java.util.List<Events> events;

          public EventsList() {
            if (events == null)
              events = new java.util.ArrayList<Events>();
          }

          public void add(Events name) {
            events.add(name);
          }

          public java.util.List<Events> getValue() {
            return events;
          }
        }
      2. In service.EventsResource, change the method associated with GET to:

        public EventsList get() {
                EventsList eventsList = new EventsList();
                List<Events> list  = PersistenceService.getInstance().createQuery("SELECT e FROM Events e").getResultList();
                for (Events e : list) {
                    eventsList.add(e);
                }
                return eventsList;
        }

        This will ensure that all the resource representations are returned instead of a reference to the resource. Make sure to fix the imports.
That's it, our server-side project is now ready. "http://localhost:8080/WebApplication3/resources/events" now return a complete RESTful representation of all the rows from the database table EVENTS.

Lets build the client-side application next. Make sure jMaki plug-in in NetBeans IDE is already installed.
  1. In the NetBeans IDE, create a new Web project, enable "Ajax Framework" and choose the "Standard" layout for "index.jsp".  Lets say the project name is "WebApplication4".
  2. Drag-and-drop a jMaki-wrapped Google Map widget in the 'Main Content Area' and jMaki-wrapped Yahoo Button in the 'Sidebar Content Here'.
  3. Customise the widgets
    1. Add id="mymap" attribute to the Google Map widget. The updated widget looks like as shown below:

      <a:widget  name="google.map" id="mymap"
               args="{ centerLat : 37.4041960114344,
                       centerLon : -122.008194923401 }" />

      id="mymap"
      will allow the Map widget to be accessed by name later.
    2. Add args="{label:'Plot Events'}" attribute to thes Yahoo button widget. The updated widget looks like as shown below:

      <a:widget name="yahoo.button" args="{label:'Plot Events'}"/>
  4. In glue,js, add the following code to *onClick subscribe method:

    var url = jmaki.xhp + "?id=events";
    var _map = jmaki.getWidget("mymap").map;
    _map.setZoom(2);
    _map.clearOverlays();
    _map.enableInfoWindow();
       
    jmaki.doAjax({method: "GET",
        url: url,
        callback: function(_req) {          
            var xmlobject = (new DOMParser()).parseFromString(_req.responseText, "text/xml");
            var root = xmlobject.getElementsByTagName('eventsList')[0];
            var events = root.getElementsByTagName('events');
            for (var i = 0 ; i < events.length ; i++) {
                var event = events[i];
                var eventName = event.getElementsByTagName('eventName')[0].firstChild.nodeValue;
                var venue = event.getElementsByTagName('venue')[0].firstChild.nodeValue;
                var blogs = event.getElementsByTagName('blogs')[0].firstChild.nodeValue;
                var dates = event.getElementsByTagName('dates')[0].firstChild.nodeValue;
               
    var id = event.getElementsByTagName('id')[0].firstChild.nodeValue;
                   
                var encodedLocation = encodeURIComponent("location=" + venue);
                var url = jmaki.xhp + "?id=yahoogeocoder&urlparams=" + encodedLocation;
                jmaki.myHandler(url, eventName, blogs, dates, id, _map);
            }
        }
    });
  5. Add the following functions above the *onClick subscribe method:

    // "Function closure" used from http://econym.googlepages.com/basic1.htm
    // Creates local copy of "marker" and "html" variables to be preserved for later use
    function createMarker(point,html) {
        var marker = new GMarker(point);
        GEvent.addListener(marker, "click", function() {
            marker.openInfoWindowHtml(html);
        });
        return marker;
    };

    // Function closure that preserves "eventName", "blogs", "dates and "id"
    // Gets the latitude/longitude from Yahoo Geocoding service and plots them on the map
    // Also creates meaningful markers
    jmaki.myHandler = function(_url, eventName, blogs, dates, id, _map) {
        jmaki.doAjax({url: _url,
            callback : function(req) {
                if (req.responseText.length > 0) {
                    jmaki.log("name: " + eventName);
                    var response = eval("(" + req.responseText + ")");
                    var coordinates = response.coordinates;
                    jmaki.publish("/jmaki/plotmap", coordinates);
                    jmaki.log("plotting " + eventName);
                    var latlng = new GLatLng(coordinates[0].latitude, coordinates[0].longitude);
                   
                    var blogHtml = "";
                    b = blogs.split(', ');
                    for (i=0; i<b.length; i++) {
                        blogHtml += '<a href="' + b[i] + '">' + (i+1) + '</a>';
                        if (i<b.length-1)
                            blogHtml += ", ";
                    }
                   
                    var txt = '<table>' +
                    '<tr><td>#' + id + ": " + eventName + '</td></tr>' +
                    '<tr>Dates: ' + dates + ', 2007</td></tr>' +
                    '<tr><td>Blogs: ' + blogHtml + '</td></tr>' +
                    '</table>';
                   
                    var marker = createMarker(latlng, txt);
                    _map.addOverlay(marker);
                    marker.openInfoWindowHtml(txt);
                } else {
                    jmaki.log("Failed to get coordinates for " + location );
                }
            }
        });   
    };
  6. Add the following entry in Web Pages, resources, xhp.json:

    ,
    {"id": "events",
     "url":"http://localhost:8080/WebApplication3/resources/events/"
    }


    assuming WebApplication3 is the project where RESTful Web service endpoint is hosted.
That completes our client-side web application as well. Now, either hit F6 (default key to Run the NetBeans project) and this will show http://localhost:8080/WebApplication4/index.jsp in the configured browser. Once you click on "Plot Events" button, all the markers on the Google Map are plotted.
Future Improvements
  1. If Jersey can return all the resource representations directly, then the workaround used above may not be required.
  2. Use e4x after <script type="text/javascript"> in index.jsp can be generated as <script type="text/javascript; e4x=1">.
  3. Build the client-side application using Rails once issue #309 & #310 are resolved.
    1. Once deployed as Rails application on WEBrick, create a WAR file and deploy on GlassFish.
    2. Try this application using GlassFish v3 gem.
  4. Embed Google Map in the blog entry.
An alternate title of this blog entry could've been "How I spent my winter break ?". But in order to keep the title inline with rest of other entries (keeping it simple and reflecting the content of the entry) I decided to use the existing title ;-)

Technorati: screencast conf jmaki jersey netbeans glassfish jpa javascript googlemaps restful web2.0 jmakimashups

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

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 |
|

http://blogs.sun.com/arungupta/date/20071001 Monday October 01, 2007

Europe Summer 2007 Trip Report

Here is a summary of my Europe Summer 2007 Trip Report:

GlassFish is the common theme between all these events, sessions and cities - that's what I was talking/preaching/demonstrating in all of them :) Here are all the detailed blogs covering each event:

During my travel, I posted several tips that are available at:

All my travel tips to Europe are aggregated here.

Technorati: conf glassfish netbeans jmaki metro jersey jruby suntechdays railsconfeurope

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

http://blogs.sun.com/arungupta/date/20070926 Wednesday September 26, 2007

Sun Tech Days Rome 2007

Sun Tech Days Rome 2007 was kick started by Maria Grazia Filippini (Director General, Sun Microsystems Italy) to a room full of approx 300 audience. She was followed by Corrado Sterpetti (Software Practice Manager, Sun Microsystems Italy). Both of them addressed the attendees in the local language. I could not understand any part of the talk (except bongiorno) but the passion was quite visible in their tone. And this is what brings Sun Tech Days back to Italy.

Reggie explained that this is 4th Sun Tech Days in Italy which is exactly the number of times Italy has won FIFA World Cup so far. It was fun when he divided the room in two halves and made them say "World" and "Cup" as he was waving his hand from left to right and wishing for Italy to win the 5th World cup in 2010.

The two main goals of the day set early in the day were:

  • Learn and Immerse in the different technologies presented.

  • Talk to one of the presenters and share with them how they can help you.

If you have attended any of the JavaOne, this is how John Gage sets the tone as well. And truly, this is one of the main objectives of taking Sun Tech Days all over the world. It's all about community and participation.

The first event of the day was a demo shoot out where 5 engineers demonstrated cool technology demonstrations. Here are the technologies that were demonstrated:

Arun Gupta (me) jMaki - Sun Tech Days Event Map
Sang Shin NetBeans Profiler
Leonid Lenyashin DTracelet in Sun Studio 12
Sridhar Reddy SunSPOT
Inyoung Cho Java ME Technologies

Then the day were split into three different tracks - Java, Solaris and Hands-on-Labs.

The first talk in Java Track was presented by Sridhar on "Java EE 5, GlassFish and Their Future". GlassFish is an open-source, production-quality and JavaEE 5 compatible Application Server. GlassFish V2 is now available and has lots of cool features. This one slide captures all of them and Sridhar gave details on all of them. He also gave a preview of what's coming in GlassFish V3. GlassFish V2 Final can be downloaded from here. You can try a Technology Preview build of GlassFish V3 here. If you have any questions about GlassFish, please post them at users@glassfish or GlassFish Forum.

Sridhar's talk was followed by my talk on "Metro and REST" where I explained how Metro is one-stop shop for all Web services needs - from simple Hello World to Secure, Reliable, Transactional and .NET 3.0 Interoperable Web services. The talk built upon Sridhar's demo where he showed how a simple Web service can be built and invoked using NetBeans 6. This talk demonstrated how Reliability and other enterprise features can be simply added by a click of button. It also demonstrated how an Excel 2007 client can invoke a Secure and Reliable endpoint deployed on GlassFish. This talk also provided a brief introduction to Jersey (Reference Implementation for JSR 311) and showed code samples on how it enables to easily and intuitively publishing RESTful endpoints. The slides for my talk are available here.

I had to leave right after my talk to catch the flight to Milan and so that's my summary of Tech Days Rome. I hope you guys had fun and we empowered you to be more productive. Feel free to leave comments if you attended and enjoyed.

GlassFish Day and NetBeans Day starts in Milan at 1pm local time today. The complete agenda is here. Looking forward to see you there.

Here is the album for Tech Days Rome 2007:

Technorati: suntechdays rome glassfish metro webservices jersey netbeans jmaki glassfish

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

http://blogs.sun.com/arungupta/date/20070910 Monday September 10, 2007

TOTD #8: Generating JSON using JAXB annotations in Jersey

Jersey provides a pluggable type system for the encoding/decoding of a Java type to/from an entity of an HTTP response/request. JSON support was added in Jersey using the BadgerFish convention for encoding/decoding JAXB beans to/from JSON. A new sample was also added in the recently released 0.2.1 that demonstrates this concept.

This TOTD provides provides a trivial sample (using the snippets shown on BadgerFish) that will allow you to experiment with this feature in Jersey. This sample consists of a Resource, Server and "build.xml" to build, deploy, run the server and invoke the endpoint.

Here is the code for the REST resource:

import javax.ws.rs.HttpMethod;
import javax.ws.rs.ProduceMime;
import javax.ws.rs.UriTemplate;

@UriTemplate("/helloworld")
public class HelloWorldResource {

  @HttpMethod
  @ProduceMime("application/xml")
  @UriTemplate("/xml")
  public States getXML() {
    return new alice();
  }

  @HttpMethod
  @ProduceMime("application/json")
  @UriTemplate("/json")
  public States getJSON() {
    return new alice();
  }
}

This resource is published on two URIs - one using XML (/xml) and the other using JSON (/json) representation. Here is the code to start the server:

import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.ws.rest.api.container.ContainerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;

public class HelloWorld {
  public static void main(String[] args) throws IOException {
    HttpHandler handler = ContainerFactory.createContainer(
      HttpHandler.class,
      HelloWorldResource.class);

    HttpServer server = HttpServer.create(new InetSocketAddress(9998), 0);
    server.createContext("/", handler);
    server.setExecutor(null);
    server.start();

    System.out.println("Server running");
    System.out.println("Visit: http://localhost:9998/helloworld");
    System.out.println("Hit return to stop...");
    System.in.read();
    System.out.println("Stopping server");
    server.stop(0);
    System.out.println("Server stopped");
  }
}

This is a very standard code that is available in all the bundled samples. And here is the "build.xml":

<project name="json" default="default" basedir=".">
  <property name="jersey.home" value="c:\jersey-0.2.1-ea"/>
  <path id="jersey.classpath">
    <fileset dir="${jersey.home}\lib" includes="*.jar"/>
  </path>
  <property name="build.dir" value="build"/>
  <property name="src.dir" value="src"/>

  <target name="init">
    <mkdir dir="${build.dir}"/>
  </target>

  <target name="build" depends="init">
    <javac srcdir="${src.dir}" destdir="${build.dir}" includes="**/*.java">
      <classpath refid="jersey.classpath"/>
    </javac>
  </target>

  <target name="run" depends="build">
    <java classname="samples.HelloWorld" fork="true">
      <classpath>
        <path refid="jersey.classpath"/>
        <pathelement location="${build.dir}"/>
      </classpath>
    </java>
  </target>

  <target name="get-json">
    <get src="http://localhost:9998/helloworld/json" dest="out.json"/>
  </target>

  <target name="get-xml">
    <get src="http://localhost:9998/helloworld/xml" dest="out.xml"/>
  </target>

  <target name="clean">
    <delete quiet="true" dir="${build.dir}"/>
  </target>
</project>

Make sure to set the value of "jersey.home" property correctly in this file. And now the JAXB bean and corresponding XML and JSON representation:

JAXB Bean XML representation JSON representation
@javax.xml.bind.annotation.XmlRootElement
public class alice {

  @javax.xml.bind.annotation.XmlValue
  protected String value;

  public alice() { this.value = "bob" }
  public String getValue() { return value; }
}

<alice>bob</alice>

{"alice":{"$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {

  @javax.xml.bind.annotation.XmlElement
  protected String bob;

  @javax.xml.bind.annotation.XmlElement
  protected String david;

  public alice() {
    bob = "charlie";
    david = "edgar";
  }

  public String getBob() { return bob; }
  public String getDavid() { return david; }
}
<alice>
  <bob>charlie</bob>
  <david>edgar</david>
</alice>
{"alice":{"bob":{"$":"charlie"},"david":{"$":"edgar"}}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {
  @javax.xml.bind.annotation.XmlValue
  protected String value;

  @javax.xml.bind.annotation.XmlAttribute
  protected String charlie;

  public alice() {
    value = "bob";
    charlie = "david";
  }

  public String getValue() { return value; }
  public String getCharlie() { return charlie; }
}
<alice charlie="david">
  bob
</alice>
{"alice":{"@charlie":"david","$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement(namespace="http://some-namespace")
public class alice {
  @javax.xml.bind.annotation.XmlValue
  protected String value;

  public alice() { value = "bob"; }

  public String getValue() { return value; }
}
<alice xmlns="http://some
-namespace">
  bob
</alice>
{"alice":{"@xmlns":{"$":"http:\/\/some-namespace"},"$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {
  @javax.xml.bind.annotation.XmlElement
  protected java.util.List<String> bob;

  public alice() {
    bob = new java.util.ArrayList<String>();
    bob.add("charlie");
    bob.add("david");
  }

  public java.util.List<String> getBob() { return bob; }
}
<alice>
  <bob>charlie</bob>
  <bob>david</bob>
</alice>
{"alice":{"bob":[{"$":"charlie"},{"$":"david"}]}}

JSON representation can always be constructed using the JSON APIs as shown below and by adding the method to "HelloWorldResource":

import org.codehaus.jettison.json.*;

@HttpMethod("GET")
@ProduceMime("application/json")
@UriTemplate("/json2")
public JSONObject getJSONMessage() throws JSONException {
  JSONObject object = new JSONObject();
  JSONObject content = new JSONObject();
  content.put("$", "bob");
  object.put("alice", content);

  return object;
}

JAX-WS also supports JSON as a pluggable encoding.

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

Technorati: totd jersey json jax-ws REST pluggableencoding restful

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

http://blogs.sun.com/arungupta/date/20070906 Thursday September 06, 2007

Learn to REST using Jersey

Jersey is the open source JAX-RS (JSR 311) Reference Implementation for building RESTful Web services. Paul announced the availability of 0.2.1. This blog entry describe the steps to get started with Jersey and provide useful pointers for further exploration.

  1. Download a stable build from here or the latest binary from here. jersey-xxx-ea.zip is the binary distribution and jersey-snapshot-xxx-ea.zip is a snapshot of the SVN repository.

  2. The bundle comes with all the required binaries (JAR dependencies) and a good set of samples for 0.2.1. Here is a consolidated list:

    HelloWorld This is how everybody starts!
    GlassFishDB Demonstrates how to use JPA to expose the contents of a database table as a RESTful resource.
    JsonFromJaxb Demonstrates how to use JSON representation of JAXB-based resources.
    EntityProvider Demonstrates pluggable entity providers.
    Bookmark Demonstrates how to use JPA in the backend.
    Bookstore Demonstrates how to use ploymorphism with resources and views that are JSP pages.
    OptimisticConcurrency Demonstrates the application of optimistic concurrency to a web resource.
    SimpleAtomServer Simple Atom server that partially conforms to the Atom Publishing Format and Protocol
    SimpleConsole Demonstrates how to use the Lightweight HTTP Server included in Sun's Java SE 6.0 release.
    SimpleJAXWSEndpoint Demonstrates how to use a JAX-WS Endpoint.
    SimpleServlet Demonstrates how to use a Servlet container.
    StorageService Demonstrates a basic in-memory web storage service

    Pick a sample, most likely "Hello World", and then follow the instructions in the main "examples" directory to run the sample.

  3. Here are some other useful pointers:
  4. As Alexis pointed, GlassFish is the preferred platform for deploying Jersey endpoints and NetBeans 6 will soon support Jersey.

Paul explained Why the name Jersey ? - Jersey is a restful place to spend a couple of days vacation. The Yellow jersey or Maillot jaune (the project logo) is worn by the overall leader in Le Tour. With modesty I would like this to be the leading open source project for developing RESTful Web services using plain old Java objects.

And then it could also be Java basEd RESTful Services for You as coined by Sridatta :)

Technorati: jersey jsr311 REST restful glassfish netbeans

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

Valid HTML! Valid CSS!

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