Thursday Feb 18, 2010

TOTD #124: Using CDI + JPA with JAX-RS and JAX-WS

This is a follow up blog to TOTD #120 and TOTD #123. These two blogs together have created a simple Java EE 6 application and showed the following features so far:

  • No-interface view for EJB
  • EJBs packaged in a WAR file
  • Optional "faces-config.xml" for Java Server Faces
  • FacesServlet registered using Servlet 3.0 programmatic registration APIs
  • Java Server Faces navigation rules using convention-over-configuration
  • Optional "web.xml" for Servlets 3.0
  • Add database access using Java Persistence API 2.0
  • Show type-safe Criteria API from JPA 2.0
  • Use Context & Dependency Injection for JSF managed beans
  • Add Ajax effects from Java Server Faces 2.0
  • Add Bean Validation to the JSF managed bean

GlassFish v3 is the Java EE 6 Reference Implementation and comes bundled with a complete SOAP Web services stack (Metro/JAX-WS) and a RESTful stack (JAX-RS/Jersey). This blog will update the previously created Maven project with:

  • A SOAP Web service using JAX-WS
  • A RESTful Web service using JAX-RS
  • Use Context & Dependency Injection with JAX-WS and JAX-RS
  • Query the database using JPA 2 based upon criteria from the Web service invocation

Lets get started!

  1. Use the Maven project from TOTD #123 and update the directory structure as follows:
    src
    src/main
    src/main/java
    src/main/java/org
    src/main/java/org/glassfish
    src/main/java/org/glassfish/samples
    src/main/java/org/glassfish/samples/ActorResource.java
    src/main/java/org/glassfish/samples/RESTApplication.java
    src/main/java/org/glassfish/samples/SakilaBean.java
    src/main/java/org/glassfish/samples/SimpleBean.java
    src/main/java/org/glassfish/samples/SimpleEJB.java
    src/main/java/org/glassfish/samples/SimpleServlet.java
    src/main/java/org/glassfish/samples/SOAPService.java
    src/main/resources
    src/main/webapp
    src/main/webapp/index.jsp
    src/main/webapp/index.xhtml
    src/main/webapp/sakila.xhtml
    src/main/webapp/show.xhtml
    src/main/webapp/WEB-INF
    src/main/webapp/WEB-INF/beans.xml
    src/main/webapp/WEB-INF/web.xml
    

    The changes are:
    • "ActorResource.java" is added for the RESTful representation of Actor table.
    • "SOAPSevice.java" is added to invoke the SOAP-based Web service.
    • "SakilaBean.java" is updated to query the database for an Actor identified by "id".
  2. The updated files are explained below.
    • A new method is added to SakilaBean.java as shown below:
       public Actor findActorById(int id) {
          EntityManager em = emf.createEntityManager();
      
          CriteriaBuilder cb = emf.getCriteriaBuilder();
          CriteriaQuery<Actor> criteria = cb.createQuery(Actor.class);
      
          // FROM clause
          Root<Actor> actor = criteria.from(Actor.class);
      
          // SELECT clause
          criteria.multiselect(actor.<Short>get("actorId"),
                               actor.<String>get("firstName"),
                               actor.<String>get("lastName"));
      
          // WHERE clause
           criteria.where(cb.equal(actor.<Short>get("actorId"), id));
      
          Query q = em.createQuery(criteria);
          ((org.eclipse.persistence.jpa.JpaQuery)q).getDatabaseQuery().dontMaintainCache();
      
          return (Actor)q.getResultList().get(0);
      }
      

      This method queries the database for an actor by his id and uses the typesafe Criteria API to achieve the purpose. The FROM, SELECT, and WHERE clause are highlighted in the code. A cast to EclipseLink specific class is required because of the bug #303205.
    • SOAPService.java
      package org.glassfish.samples;
      
      import javax.inject.Inject;
      import javax.jws.WebService;
      import sakila.Actor;
      
      @WebService
      public class SOAPService {
          @Inject SakilaBean bean;
      
          public String sayHello(int id) {
              Actor a = bean.findActorById(id);
              return "Hello " + a.getFirstName();
          }
      }
      

      The key points in the code are:
      • Standard JAX-WS annotations from "javax.jws.\*" package are used to represent the Web service.
      • The Web service has only one method "sayHello" that concatenates the string "Hello" with the first name of "Actor" identified by "id".
      • No deployment descriptor modifications are required to publish this Web service.
      • "SakilaBean" is injected using @Inject annotation and used to query the database. This allows to encapsulate all the database details in one class and injected in a typesafe manner.
    • RESTApplication.java
      package org.glassfish.samples;
      
      import javax.ws.rs.ApplicationPath;
      import javax.ws.rs.core.Application;
      
      @ApplicationPath("/sakila")
      public class RESTApplication extends Application {
      }
      

      This is a marker class to inform Jersey of the root resource to be registered. By default, all classes with @Path and @Provider annotations are included. It also specifies the base path at which all resources are accessible.

      An alternative to this class is to specify the required information in "web.xml" as:
      <servlet>
           <servlet-name>Jersey Web Application</servlet-name>
           <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
       </servlet>
      
       <servlet-mapping>
           <servlet-name>Jersey Web Application</servlet-name>
           <url-pattern>/sakila/\*</url-pattern>
       </servlet-mapping>
      

      So only one of RESTApplication.java or changes in "web.xml" are required.
    • ActorResource.java
      package org.glassfish.samples;
      
      import javax.enterprise.context.RequestScoped;
      import javax.inject.Inject;
      import javax.ws.rs.GET;
      import javax.ws.rs.Path;
      import javax.ws.rs.Produces;
      import javax.ws.rs.PathParam;
      import sakila.Actor;
      
      @Path("/actor/{id}")
      @RequestScoped
      public class ActorResource {
          @Inject SakilaBean sakila;
      
          @GET
          @Produces("application/json")
          public Actor getActor(@PathParam("id") int id) {
              return sakila.findActorById(id);
          }
      }
      
      The key points in the code are:
      • Standard JAX-RS annotations from "javax.ws.rs" package are used to represent the RESTful resource.
      • "getActor" method is invoked when the resource is accessed using HTTP GET.
      • The resource is accessible at "/actor/{id}" URL where "{id}" is mapped to the "id" parameter of "getActor" method.
      • SakilaBean is injected in a typesafe manner using @Inject annotation. This bean is then used to query the database using the "id" parameter.
      • "getActor" method produces JSON representation, as defined by the "@Produces" annotation. This is easily achieved by updating our Persistence Unit (PU) created in TOTD #122 and adding "@javax.xml.bind.annotation.XmlRootElement" as the class level annotation on "sakila.Actor" class. Make sure to install the updated PU to your local Maven repository.

Package and deploy the application as:
mvn clean package
./bin/asadmin deploy --force=true ~/samples/javaee6/simplewebapp/target/simplewebapp-1.0-SNAPSHOT.war

Now the SOAP web service is accessible at "http://localhost:8080/simplwebapp-1.0-SNAPSHOT/SOAPServiceService" and looks like:


Notice, the URL in your case may be different if the Web service class name was different. The default URL is "http://<HOST>:<PORT>/<CONTEXT ROOT><WEB SERVICE CLASS NAME>Service".

This Web service can be easily tested by using the in-built tester accessible at "http://localhost:8080/simplwebapp-1.0-SNAPSHOT/SOAPServiceService?tester" and looks like:

The WSDL describing the Web service can be seen by clicking on the "WSDL File" link. The Web service method can be invoked by entering a value ("id" of the Actor) in the text box and clicking on "sayHello" button. Here is a sample run:

Clicking on "Submit" invokes the Web service which then uses the injected "SakilaBean" to query the database using the parameter specified. The first name from the response from the database is then extracted, concatenated with the string "Hello" and returned as Web service response.

The RESTful resource is accessible at "http://localhost:8080/simplwebapp-1.0-SNAPSHOT/sakila/actor/5" and looks like:

As in the SOAP-based Web service, the "5" in the URL is mapped to a parameter in the "ActorResource.java", the injected "SakilaBean" is then used to query the database and returns the JSON representation. Specifying a different number in the URL will show the RESTful JSON representation for that particular actor.

More Java EE 6 features will be covered in subsequent blogs. Are you interested in any particular ones ?

Technorati: jaxws metro webservices jaxrs rest jersey glassfish v3 cdi jsr299 weld

Monday Dec 07, 2009

TOTD #116: GlassFish v3 Administration using JavaFX front-end - JNLP available

As mentioned in TOTD #113, this Tip Of The Day (TOTD) provides a working version of the JavaFX front-end for GlassFish v3 administration.

Please click here to launch the JNLP or click here to a page that provides some introduction along with the link to JNLP. You may like to enable Java Console as explained in TOTD #114 for any log messages.

See a video of the tool in action:

Many thanks to Rajeshwar for providing feedback and helping me understand the RESTful interface better. TOTD #96 explains how the REST interface can be used.

Here is a TODO list in no particular order:

  • Show a splash screen after the startup to indicate server status
  • Allow the administration host/port to be changed
  • Tie the "server stats" with the server uptime instead of fetching once and then binding it locally
  • Provide dynamic updates of the monitoring data, currently its a snapshot
  • Convert the monitoring levels text boxes to radio buttons
  • Provide complete hints on setting monitoring level based upon the engines
  • Enable/Disable the buttons based upon the status of server running (or not)
  • Introduce charts to track dynamic shrink/expand of threads/pools/etc.
  • Probably something else that I'm forgetting :-)

How are you using JavaFX with GlassFish ?

How will you use GlassFish v3 REST interface in your tools ?

Technorati: totd javafx glassfish v3 rest web jruby rubyonrails rest administration monitoring management

Tuesday Nov 10, 2009

TOTD #113: JavaFX front-end for GlassFish v3 Administration - Using REST interface

GlassFish v3 provides a REST interface to management and monitoring information as discussed in TOTD #96. As mentioned in that blog "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". This blog introduces a tool that uses the REST API to provide management and monitoring of GlassFish v3 and is written using JavaFX.

This tool is only a proof-of-concept that demonstrates that GlassFish v3 REST interface is functionally very rich and can indeed be used to write third-party administration tools. The tool uses a subset of the REST interface and exposes only a limited amount of management and monitoring capabilities otherwise exposed. After all this is a proof-of-concept :-)

A screencast of this tool in action along with a downloadable JNLP version will soon be available. For now, here is a snapshot of the main window of this tool:

The main screen allows you to enter a URL for the GlassFish administration. Then the GlassFish instance can be stopped/restarted from the main window using the buttons on top right. There is an animation at the bottom of the screen where the glassfish is swimming in the ocean and is directly related to the state of server running in the background. If the server is running, the animation works. If the server is not running then the animation stops as well.

The main screen has three main buttons:

  • "List Applications" - list all the applications deployed on the running instance
  • "Show Monitoring Levels" - show/Update all the monitoring levels
  • "Server Stats" - show statistics of the running server

Clicking on "List Applications" shows the list of applications deployed on this particular instance. Here is how a snapshot looks like for an instance running on my localhost at port 4848:

As shown in the screen, it shows a radio-bulleted list of all the applications. Each bullet is also accompanied by an image indicating the type of application - Web or Rails for now. Select the application and click on "Monitor" button to monitor that particular application. The REST API exposes a vast amount of monitoring data but a subset of monitoring data is displayed for Web and Rails application for now. Here is a snapshot of the monitoring data published for a Web application:

As evident by the list of engines, this web application has EJBs bundled as well. It also shows total number of Servlets/JSPs loaded, number of requests made to this web application and some other monitoring data.

Here is a snapshot of the monitoring data published for a Rails application:

It shows number of JRuby runtimes configured for the application, number of requests sent to the application, number of responses with different HTTP access codes and some other data.

The monitoring levels of different containers can be easily updated by clicking on "Show Monitoring Levels" as shown below:

And finally some server statistics are shown by clicking on "Server Stats" as shown below:

It shows when the server was started, host/port information, version and finally how long the server has been running for. The dials are an animation that shows the server up time.

Here are other related JavaFX and GlassFish related blogs published earlier:

How are you going to use the REST interface exposed by GlassFish v3 in your environment ?

Are you using JavaFX with GlassFish together in any way ?

Leave a comment on this blog if you do!

Technorati: javafx glassfish v3 rest web jruby rubyonrails rest administration monitoring management

Thursday Oct 08, 2009

TOTD #112: Exposing Oracle database tables as RESTful entities using JAX-RS, GlassFish, and NetBeans

This Tip Of The Day explains how to expose an existing Oracle database table as a RESTful Web service endpoint using NetBeans tooling and deployed on GlassFish.

Lets get started!

  1. Configure GlassFish v3 10/7 or a later nightly in a recent NetBeans 6.8 build (latest nightly). As issue# 9885 is fixed, so copy ojdbc6.jar in the "domains/domain1/lib/ext" directory.
  2. Create a Web application
    1. Create a new "Web application" and name the project "RestfulOracle":



      click on "Next >".
    2. Choose the newly added server and "Java EE 6 Web" as the Java EE version:



      and click on "Finish".
  3. Create JPA entities for "HR" schema. The steps outlined below uses NetBeans solely for creating the JPA entities. Alternatively, TOTD #108 explains how to define a JDBC connection pool and JDBC resource using "asadmin" CLI and then use that resource from within NetBeans. Either way, the JDBC resource is stored in the underlying "domain.xml".
    1. Right-click on the project and select "New", "Entity Classes from Database...".
    2. In "Data Source:" select "New Data Source..." as shown below:

    3. Specify the JNDI name as "jdbc/hr" and choose the pre-configured database connection as shown below:



      TOTD #107 explains how to configure Oracle database in NetBeans.
    4. In the list of "Available Tables:", select "EMPLOYEES" and click on "Add >" to see the following:



      Notice the list of related tables are included as well. Click on "Next >".
    5. Specify the package name as "model".
    6. Click on "Create Persistence Unit...", take the defaults, and click on "Create":



      and click on "Finish". Notice EclipseLink, the reference implementation for JPA 2.0, is used as the persistence provider. This generates POJOs that provide database access using JPA 2.0 APIs. These APIs are included as part of the Java EE 6 platform.
  4. Create RESTful entities
    1. Right-click on the project and select "RESTful Web Services from Entity Classes...":


    2. Select "Employees (model.Employees)" from "Available Entity Classes:" and click on "Add >" to see the following:



      click on "Next >", take the defaults, and click on "Finish". This generates a bunch of wrapper classes using JAX-RS to expose the JPA Entity classes as RESTful Web services. JAX-RS 1.1 is also included as part of the Java EE 6 platform.
  5. Run the Web service
    1. Right-click the project and select "Test RESTful Web Services":



      This deploys the created Web application on the selected GlassFish build and displays the following page in the default browser:


    2. Click on "deparmentss" and then on "Test" button to see the output as:



      Clicking the "Test" button issues a GET request to "http://localhost:8080/RestfulOracle/resources/departmentss". This uses the generated JAX-RS wrapper classes to talk to the database using JPA entity classes and query the first 10 rows from the "DEPARTMENTS" table. The response is then JSON formatted using JAX-RS wrapper classes and is returned to the requesting page which then displays it nicely formatted in the table. It also shows l-level deep department's relationship to other entities. If the "expandLevel" on the above page is set to "0", then the following output is shown:



      The "Raw View" (JSON data) of the original output looks like:



      Notice this is the raw JSON output generated by the JAX-RS wrapper classes. The "Http Monitor" traffic looks like:



      The format of data returned can be changed from "application/json" to "application/xml" as shown below:



      And even a POST request can be generated.

Do you have the need to expose your Oracle database tables as RESTful entities ?

A complete archive of all the TOTDs is available here.

This and other similar applications will be demonstrated at the upcoming Oracle Open World.

Technorati: totd oracle database glassfish v3 netbeans javaee jax-rs jpa rest

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

Sunday Nov 30, 2008

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


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

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

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

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

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

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

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

How are you using Jersey ?

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

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

Technorati: totd glassfish v3 embeddable jersey jsr311 rest json webservices

Tuesday Nov 25, 2008

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

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

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

      package org.glassfish.samples;

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

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

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

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

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

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

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

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

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

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

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

      Results :

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

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

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

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

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

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

Technorati: totd glassfish v3 embeddable jersey jsr311 rest json webservices

Monday Nov 24, 2008

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


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

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

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

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

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

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

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

      are changed to:

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

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

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

      package org.glassfish.samples;

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

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

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

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

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

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

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

      package org.glassfish.samples;

      import javax.xml.bind.annotation.XmlRootElement;

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

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

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



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

    mvn clean package

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

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

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

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

Technorati: totd glassfish v3 embeddable jersey jsr311 rest json webservices

Friday Oct 17, 2008

SOAP and REST - both equally important to Sun


"Sun moving away from SOAP to embrace REST" is the misleading title of an article recently published in SD Times. The article provides a good introduction to JAX-RS and Jersey. But I really wonder what motivated the author of this article to use this title. This blog, hopefully, provides a better context.

Jersey is the Reference Implementation of Java API for RESTful Web Services (JAX-RS, JSR 311) and was released earlier this week. The headline indicates that Sun is leaving SOAP and will support REST. The debate between REST and SOAP is not new and there are religious camps on both sides (even within Sun). And that's completely understandable because each technology has its own merits and demerits. But just because a new JSR aimed to make RESTful Web services easy in the Java platform is released, it does not mean Sun Microsystems is leaving existing technology in trenches.

The addition of Jersey to Sun's software portfolio makes the Web services stack from GlassFish community a more compelling and comprehensive offering. This is in contrast  to "moving away" from SOAP as indicated by the title. As a matter of fact, Jersey will be included as part of Metro soon, the Web Services stack of GlassFish. And then you can use JAX-WS (or Metro) if you like to use SOAP or JAX-RS (or Jersey) if you prefer RESTful Web services. It's all about a offering choice to the community instead of showing a direction.

Here are some data points for JAX-WS:
  • The JAX-WS 2.0 specification was released on May 11, 2006. There have been couple of maintenance releases since then and another one brewing.
  • Parts of Metro, the implementation of JAX-WS, are currently baked into GlassFish, embeddable in JBoss WS Stack, and also part of Oracle Weblogic and IBM Websphere.
  • The implementation stack is mature and used in several key customer deployments. 
  • JAX-WS is already included in Java SE 6 and hence available to a much wider audience.
  • As opposed to "moving away", JAX-WS 2.2 (currently being worked upon) will be included in Java EE 6 platform, as will Jersey be.
So I believe both SOAP and REST are here to stay, at least in the near future. And Sun Microsystems is committed to support them!

You still think Sun is moving away from SOAP ?

It seems a personal preference is interpreted as Sun's disinvestment in SOAP. It's good to have increased readership but not at the cost of misleading headlines :)

Technorati: jax-ws rest webservices metro sdtimes glassfish

Sunday Sep 09, 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

About

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


Java EE 7 Samples

Stay Connected

Search

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