Tuesday Jul 23, 2013

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


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

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


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

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

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

The resource definition is:

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


POJO is defined as:

public class MyObject {

    private String name;
    private int age;

    public MyObject() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

Application class is:

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

And the client code is:

Client client = ClientBuilder.newClient();

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

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

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

Monday Mar 04, 2013

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


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

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

Lets say your domain object is defined as:

public class MyObject {

  private String name;
  private int age;

  //. . .
}

And your resource endpoint is defined as:

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

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

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

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

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

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

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

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

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

Download the source code and enjoy!

Monday Jan 07, 2013

JAX-RS Custom Entity Providers (TOTD #193)


JAX-RS defines Entity Providers that supply mapping services between on-the-wire representations and their associated Java types. The entities, also known as "message payload" or "payload" represent the main part of an HTTP message. These are specified as method parameters and return types of resource methods. Several standard Java types such as String, byte[], javax.xml.bind.JAXBElement, java.io.InputStream, java.io.File, and others have a pre-defined mapping and is required by the specification. Applications may provide their own mapping to custom types using MessageBodyReader and MessageBodyWriter interfaces. This allows to extend the JAX-RS runtime easily to support your own custom entity providers.

The MessageBodyReader interface defines the contract for a provider that supports the conversion of a stream to a Java type. The MessageBodyWriter interface defines the contract for a provider that supports the conversion of a Java type to a stream. This Tip Of The Day (TOTD) will explain how to write these custom entity providers.

Note, this functionality was defined in JAX-RS 1.0 as opposed to Client API, Client- and Server-side async, Hypermedia, and many other features. The complete source code for this sample application can be downloaded here and will work on GlassFish 4.0 b70.

Here is a simple resource that returns name of the fruit based upon the index passed as parameter:

@Path("fruits")
public class MyResource {
private String[] response = { "apple", "banana", "mango" };

@POST
public String getFruit(int index) {
return response[index % 3];
}
}
This resource can be invoked using the newly introduced Client API as:

Client client = ClientFactory.newClient();
WebTarget target = client.target("http://"
+ request.getServerName()
+ ":"
+ request.getServerPort()
+ request.getContextPath()
+ "/webresources/fruits");
String fruit = target
.request()
.post(Entity.text("1"), String.class);

If we update the resource such that the index parameter is passed as the following object:

public class MyObject implements Serializable {
public static final String MIME_TYPE = "application/myType";

private int index;

public MyObject() {
}

public MyObject(int index) {
this.index = index;
}

public int getIndex() {
return index;
}

public void setIndex(int index) {
this.index = index;
}
}

Then the resource method would look like:

@POST
@Consumes(value=MyObject.MIME_TYPE)
public String getFruit(MyObject mo) {
return response[Integer.valueOf(mo.getIndex()) % 3];
}

The additional @Consumes annotation defines the media type that the method can accept. The following custom entity providers are defined to support this custom type. The first one, MyReader, defines the conversion of on-the-wire representation to MyObject Java type.

@Provider
@Consumes(MyObject.MIME_TYPE)
public class MyReader implements MessageBodyReader<MyObject> {

@Override
public boolean isReadable(Class<?> type, Type type1, Annotation[] antns, MediaType mt) {
return MyObject.class.isAssignableFrom(type);
}

@Override
public MyObject readFrom(Class<MyObject> type,
Type type1,
Annotation[] antns,
MediaType mt, MultivaluedMap<String, String> mm,
InputStream in) throws IOException, WebApplicationException {
try {
ObjectInputStream ois = new ObjectInputStream(in);
return (MyObject)ois.readObject();
} catch (ClassNotFoundException ex) {
Logger.getLogger(MyReader.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}

The interface require two methods, isReadable and readFrom, to be implemented. The implementation has @Consumes annotation restricting the media type supported by this entity provider.

The second one, MyWriter, defines the conversion from MyObject Java type to on-the-wire representation.

@Provider
@Produces(MyObject.MIME_TYPE)
public class MyWriter implements MessageBodyWriter<MyObject> {

@Override
public boolean isWriteable(Class<?> type, Type type1, Annotation[] antns, MediaType mt) {
return MyObject.class.isAssignableFrom(type);
}

@Override
public long getSize(MyObject t, Class<?> type, Type type1, Annotation[] antns, MediaType mt) {
// As of JAX-RS 2.0, the method has been deprecated and the
// value returned by the method is ignored by a JAX-RS runtime.
// All MessageBodyWriter implementations are advised to return -1 from
// the method.

return -1;
}

@Override
public void writeTo(MyObject t,
Class<?> type,
Type type1,
Annotation[] antns,
MediaType mt,
MultivaluedMap<String, Object> mm,
OutputStream out) throws IOException, WebApplicationException {
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(t);
}
}
The interface require three methods isWriteable, getSize, writeTo to be implemented. The implementation of getSize method is recommended to return -1 as JAX-RS runtime is required to compute the actual Content-Length header value. The implementation has @Produces annotation restricting the media type supported by this entity provider.

The reader and writer entity providers are marked with @Provider and are thus automatically discovered at runtime on the server-side. But until JERSEY-1634 is fixed, they need to be explicitly specified in the Application class as:

@ApplicationPath("webresources")
public class MyApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
resources.add(MyResource.class);
resources.add(MyReader.class);
resources.add(MyWriter.class);
return resources;
}
}

On the client-side, the providers need to be explicitly registered as shown:

Client client = ClientFactory.newClient();
client
.configuration()
.register(MyReader.class)
.register(MyWriter.class);
String fruit = target
.request()
.post(Entity.entity(new MyObject(1), MyObject.MIME_TYPE), String.class);

Notice, how invoking the endpoint requires to specify the media type as part of post method invocation.

The complete source code for this sample application can be downloaded here and will work on GlassFish 4.0 b70.

Here are some more pointers to follow

Provide feedback on Jersey 2 to users@jersey.java.net and JAX-RS specification to users@jax-rs-spec.java.net.


Thursday Jul 05, 2012

Jersey 2 in GlassFish 4 - First Java EE 7 Implementation Now Integrated (TOTD #182)


The JAX-RS 2.0 specification released their Early Draft 3 recently. One of my earlier blogs explained as the features were first introduced in the very first draft of the JAX-RS 2.0 specification. Last week was another milestone when the first Java EE 7 specification implementation was added to GlassFish 4 builds.

Jakub blogged about Jersey 2 integration in GlassFish 4 builds. Most of the basic functionality is working but EJB, CDI, and Validation are still a TBD. Here is a simple Tip Of The Day (TOTD) sample to get you started with using that functionality.
  1. Create a Java EE 6-style Maven project

    mvn archetype:generate
    -DarchetypeGroupId=org.codehaus.mojo.archetypes
    -DarchetypeArtifactId=webapp-javaee6 -DgroupId=example
    -DartifactId=jersey2-helloworld -DarchetypeVersion=1.5
    -DinteractiveMode=false

    Note, this is still a Java EE 6 archetype, at least for now.
  2. Open the project in NetBeans IDE as it makes it much easier to edit/add the files. Add the following <repositories>

    <repositories>
    <repository>
    <id>snapshot-repository.java.net</id>
    <name>Java.net Snapshot Repository for Maven</name>
    <url>https://maven.java.net/content/repositories/snapshots/</url>
    <layout>default</layout>
    </repository>
    </repositories>

  3. Add the following <dependency>s

    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.10</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.0-m09</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-client</artifactId>
    <version>2.0-m05</version>
    <scope>test</scope>
    </dependency>
    The complete list of Maven coordinates for Jersey2 are available here. An up-to-date status of Jersey 2 can always be obtained from here.
  4. Here is a simple resource class:

    @Path("movies")
    public class MoviesResource {

    @GET
    @Path("list")
    public List<Movie> getMovies() {
    List<Movie> movies = new ArrayList<Movie>();

    movies.add(new Movie("Million Dollar Baby", "Hillary Swank"));
    movies.add(new Movie("Toy Story", "Buzz Light Year"));
    movies.add(new Movie("Hunger Games", "Jennifer Lawrence"));

    return movies;
    }
    }
    This resource publishes a list of movies and is accessible at "movies/list" path with HTTP GET. The project is using the standard JAX-RS APIs.

    Of course, you need the trivial "Movie" and the "Application" class as well. They are available in the downloadable project anyway.
  5. Build the project

    mvn package

    And deploy to GlassFish 4.0 promoted build 43 (download, unzip, and start as "bin/asadmin start-domain") as

    asadmin deploy --force=true target/jersey2-helloworld.war

  6. Add a simple test case by right-clicking on the MoviesResource class, select "Tools", "Create Tests", and take defaults. Replace the function "testGetMovies" to
    @Test
    public void testGetMovies() {
    System.out.println("getMovies");
    Client client = ClientFactory.newClient();
    List<Movie> movieList = client.target("http://localhost:8080/jersey2-helloworld/webresources/movies/list")
    .request()
    .get(new GenericType<List<Movie>>() {});
    assertEquals(3, movieList.size());
    }

    This test uses the newly defined JAX-RS 2 client APIs to access the RESTful resource.
  7. Run the test by giving the command "mvn test" and see the output as

    -------------------------------------------------------
    T E S T S
    -------------------------------------------------------
    Running example.MoviesResourceTest
    getMovies
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.561 sec

    Results :

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

GlassFish 4 contains Jersey 2 as the JAX-RS implementation. If you want to use Jersey 1.1 functionality, then Martin's blog provide more details on that. All JAX-RS 1.x functionality will be supported using standard APIs anyway. This workaround is only required if Jersey 1.x functionality needs to be accessed.

The complete source code explained in this project can be downloaded from here.

Here are some pointers to follow

Provide feedback on Jersey 2 to users@jersey.java.net and JAX-RS specification to users@jax-rs-spec.java.net.

Wednesday Nov 17, 2010

TOTD #150: Collection of GlassFish, NetBeans, JPA, JSF, JAX-WS, EJB, Jersey, MySQL, Rails, Eclipse, and OSGi tips

This is the 150th tip published on this blog so decided to make it a collection of all the previous ones. Here is a tag cloud (created from wordle.net/create) from title of all the tips:

As expected GlassFish is the most prominent topic. And then there are several entries on NetBeans, JRuby/Rails, several Java EE 6 technologies like JPA, JAX-WS, JAX-RS, EJB, and JSF, and more entries on Eclipse, OSGi and some other tecnhologies too. Here is a complete collection of all the tips published so far:

Just for fun, here is another tag cloud:

You can access all the tips here. And keep those suggestions coming!

Technorati: totd glassfish netbeans jpa jsf jaxws jersey mysql rails osgi eclipse

Wednesday Aug 04, 2010

TOTD #143: Retrieve Twitter user timeline using using Jersey and OAuth

The Basic Authentication for authorizing with Twitter API will be turned off on Aug 16th. After that OAuth will be the only way to invoke the API.

Beginner's guide to OAuth provide an excellent explanation to OAuth. The typical analogy for OAuth is a "valet key" to the car which is a stripped down version of your regular key. These keys are meant for valet drivers who don't need to open trunk or glove compartment and don't need to drive the car for longer distance. So even though they have access to the entire car but are restricted to the limited functionality.

OAuth is used to share your resources (photos, videos, bank accounts, etc) stored on one site with another site without having to share your username and password. The site storing the resources is "Service Provider", the site requesting the access is "Consumer", you are the "User", "Tokens" are "valet key" that provide required access to the resources.

This Tip Of The Day (TOTD) explains how Jersey, the Reference Implementation for JAX-RS, provides seamless support for OAuth by creating a simple desktop application that retrieves user timeline on Twitter using OAuth. This blog is going to combine the instructions outlined in Understanding the guts of Twitter's OAuth for client apps and Using Jersey client OAuth support with Smugmug to achieve that.

Lets get started!

  1. Create a Maven project as:
    mvn -DarchetypeVersion=1.0 -DgroupId=org.glassfish.samples -DarchetypeArtifactId=maven-archetype-quickstart -Dversion=1.0-SNAPSHOT -DarchetypeGroupId=org.apache.maven.archetypes -Dpackage=org.glassfish.samples.twitter -DartifactId=twitter
    
  2. Update the generated "pom.xml" with the following fragments:
    <repositories>
      <repository>
        <id>glassfish-repository</id>
        <name>Java.net Repository for Glassfish</name>
        <url>http://download.java.net/maven/2/</url>
      </repository>
    </repositories>
    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.1.3-SNAPSHOT</version>
      </dependency>
      <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-json</artifactId>
        <version>1.1.3-SNAPSHOT</version>
      </dependency>
      <dependency>
        <groupId>com.sun.jersey.oauth</groupId>
        <artifactId>oauth-signature</artifactId>
        <version>1.1.2-ea-SNAPSHOT</version>
      </dependency>
      <dependency>
        <groupId>com.sun.jersey.oauth</groupId>
        <artifactId>oauth-client</artifactId>
        <version>1.1.2-ea-SNAPSHOT</version>
       </dependency>
    </dependencies>
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>2.0.2</version>
          <configuration>
            <source>1.6</source>
            <target>1.6</target>
          </configuration>
        </plugin>
      </plugins>
     </build>
    

    The Jersey dependencies add the core Jersey libraries and OAuth functionality in Jersey.
  3. Register your app with Twitter - Register your application with Twitter by clicking on Register a new application >>. The complete list of registered applications can be seen at Applications using Twitter. Select "Client" as the app type, select "Yes, use Twitter for login" and leave the "Callback URL" empty. The registration gives you "consumer key" and "consumer secret". These are used to obtain temporary credentials (or request tokens) from Twitter.
  4. Obtain Twitter OAuth credentials - Each OAuth request is an HTTP request with "Authorization" header specifying the information by OAuth service provider. Jersey provides a OAuthClientFilter to add this header to the outbound client request. Twitter API Wiki explains the authentication as multiple step process for desktop applications. Each step involves sending some parameters to twitter and getting a result back and the intent of each method/request is clearly explained in Understanding the guts of Twitter's OAuth for client apps. In our case, each request is created by using Jersey Client API and attaching OAuthClientFilter and is explained next.
    1. Request temporary credentials, a.k.a request token, from Twitter using oauth/request_token.
      1. In "App.java", create an instance of Jersey client in the constructor and attach a LoggingFilter to dump inbound/outbound messages as:
        public App() {
            // Create a Jersey client
            client = Client.create();
        
            client.addFilter(new LoggingFilter());
        }
        
      2. Request temporary credentials by adding the following method:
        public void getRequestToken() {
            client.removeAllFilters();
        
            // Create a resource to be used to make Twitter API calls
            WebResource resource = client.resource(REQUEST_TOKEN_URL);
        
            // Set the OAuth parameters
            OAuthSecrets secrets = new OAuthSecrets().consumerSecret(CONSUMER_SECRET);
            OAuthParameters params = new OAuthParameters().consumerKey(CONSUMER_KEY).
                    signatureMethod("HMAC-SHA1").version("1.0");
            // Create the OAuth client filter
            OAuthClientFilter oauthFilter =
                    new OAuthClientFilter(client.getProviders(), params, secrets);
        
            // Add the filter to the resource
            resource.addFilter(oauthFilter);
        
            // make the request and print out the result
            System.out.println(resource.get(String.class));
        }
        
        
        Note, "OAuthClientFilter" is used to populate the "Authorization" header instead of handcrafting it. The REQUEST_TOKEN_URL is "http://twitter.com/oauth/request_token", CONSUMER_SECRET and CONSUMER_KEY are the values obtained from registering your application.
      3. Edit "AppTest.java" and change "testApp" method such that it looks like:
        public void testApp() {
            App app = new App();
            app.getRequestToken();
        }
        
      4. Obtain the temporary credentials by running this application as:
        mvn test
        

        and see an output as:
        oauth_token=REQUEST_OAUTH_TOKEN&oauth_token_secret=REQUEST_OAUTH_TOKEN_SECRET&oauth_callback_confirmed=true
        

        REQUEST_OAUTH_TOKEN, a temporary token, is used to authorize on twitter.com.
    2. Authorize the user and obtain PIN
      1. Go to "https://twitter.com/oauth/authorize?oauth_token=REQUEST_OAUTH_TOKEN" in a browser window.
      2. If not already logged in, enter your twitter credentials and click "Allow".
      3. Copy the PIN.
    3. Request permanent credentials, a.k.a access token, from Twitter using oauth/access_token.
      1. Request permanent credentials by adding the following method in "App.java"
        public void getAccessToken() {
                client.removeAllFilters();
        
                // Set the OAuth parameters
                OAuthSecrets secrets = new OAuthSecrets().consumerSecret(CONSUMER_SECRET);
                OAuthParameters params = new OAuthParameters().consumerKey(CONSUMER_KEY).
                        signatureMethod("HMAC-SHA1").
                        version("1.0").
                        token(REQUEST_OAUTH_TOKEN).
                        verifier(PIN);
                // Create the OAuth client filter
                OAuthClientFilter oauthFilter =
                        new OAuthClientFilter(client.getProviders(), params, secrets);
        
                // Create a resource to be used to make Twitter API calls
                WebResource resource = client.resource(ACCESS_TOKEN_URL);
        
                // Add the filter to the resource
                resource.addFilter(oauthFilter);
        
                // make the request and print out the result
                System.out.println(resource.get(String.class));
            }
        
        REQUEST_OAUTH_TOKEN is the temporary token obtained earlier, ACCESS_TOKEN_URL is "https://twitter.com/oauth/access_token".

        Notice, REQUEST_OAUTH_TOKEN and PIN are now added to the OAuthClientFilter.
      2. Invoke this method by editing "AppTest.java" as:
        public void testApp() {
             App app = new App();
        //     app.getRequestToken();
             app.getAccessToken();
        }
        
      3. Obtain the permanent credentials by running this application as:
        mvn test
        

        and see an output as:
        oauth_token=ACCESS_OAUTH_TOKEN&oauth_token_secret=ACCESS_OAUTH_TOKEN_SECRET&user_id=USER_ID&screen_name=USER_NAME
        

        ACCESS_OAUTH_TOKEN is the authorized token that can be used for making any future requests, USER_ID and USER_NAME are identifiers for the user who signed in on twitter.com. 
  5. Get the last 20 status messages for the user from Twitter
    1. Add the following method in "App.java:
      public void getUserTimeline() {
          client.removeAllFilters();
      
          // Set the OAuth parameters
          OAuthSecrets secrets = new OAuthSecrets().consumerSecret(CONSUMER_SECRET);
          OAuthParameters params = new OAuthParameters().consumerKey(CONSUMER_KEY).
                  signatureMethod("HMAC-SHA1").
                  version("1.0").
                  token(ACCESS_OAUTH_TOKEN);
          // Create the OAuth client filter
          OAuthClientFilter oauthFilter =
                  new OAuthClientFilter(client.getProviders(), params, secrets);
      
          // Create a resource to be used to make Twitter API calls
          WebResource resource = client.resource(USER_TIMELINE_URL);
      
          // Add the filter to the resource
          resource.addFilter(oauthFilter);
      
         // Parse the JSON array
          JSONArray jsonArray = resource.get(JSONArray.class);
          List<String> statuses = new ArrayList<String>();
      
          try {
              for (int i = 0; i < jsonArray.length(); i++) {
                  JSONObject jsonObject = (JSONObject) jsonArray.get(i);
                  StringBuilder builder = new StringBuilder();
                  builder.append(jsonObject.getString("text")).
                          append(jsonObject.getString("created_at"));
                  statuses.add(builder.toString());
              }
          } catch (JSONException ex) {
              Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
          }
      } 
      USER_TIMELINE_URL is "http://api.twitter.com/1/statuses/user_timeline.json". The "getTimelineElements" method can be updated to pick other elements from the return JSON object. The complete JSON schema for the response is described here.
    2. Edit "AppTest.java" as:
      public void testApp() {
          App app = new App();
      //    app.getRequestToken();
      //    app.getAccessToken();
          app.getUserTimeline();
      }
      
    3. Finally get the last 20 status updates by giving the command:

      mvn test
      


      and see the output similar to:
      Running org.glassfish.samples.twitter.AppTest
      [Developing OSGi-Enabled Java EE Applications- http://bit.ly/aOim34 (via 
      @JavaOneConf) #javaone10Wed Aug 04 23:53:13 +0000 2010, Google Wave goes
       bye bye (via @google:)Update on Google Wave http://bit.ly/bIoDWAWed Aug
       04 21:16:07 +0000 2010, @gdaniels Yeah, I expected #wave to bye bye as
       well, but this is fairly quick!Wed Aug 04 21:15:41 +0000 2010,
      

And that's it!

This Tip Of The Day explained how to use Jersey to retrieve last 20 status messages that a user posted on twitter. Here are some other future possible additions:

  • POST status update
  • Integrate Search API using OAuth (is it possible ?)
  • Integrate Streaming API (need more investigation)
  • Create a web-base client that automatically redirects the user from application to twitter.com and then back to the application.

Jersey and OAuth wiki provides more details about how to use OAuth with Jersey.

Technorati: totd jaxrs jersey restful webservices oauth twitter glassfish

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

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

Wednesday Dec 03, 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

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