Sunday, July 28, 2013

Java's REST Jersey 2.x examples

Jersey updated to 2.0 (JAX-RS 2.0), but most of the examples on the internet are for Jersey 1.x.  There are a few changes, none are that big, but here are some examples of serving and requesting (client) Jersey 2.0.

First, grab the Jersey jars from here: http://jersey.java.net/download.html - I got the Jersey JAX-RS 2.1 RI bundle  which contains what I needed.  Next, as I'm working in Eclipse, I created a Dynamic Web Project (right click in package explorer area -> Other Project -> Web -> Dynamic Web Project) and called it DynamicWebP. 

Extract the jars from the bundle and copy them into the project's WebContent/WEB-INF/lib directory. Also, add them to the project build path (right click on the project -> build path -> add external libraries).  For these examples, only javax.ws.rs-api-2.0.jar and jersey-container-servlet-core.jar are needed on the build path, but it's easier to add them all.  For deployment, all/nearly all are needed so it's easier to add them from the start to the WEB-INF/lib directory.  (If you have problems with some of the jars added to build path, but not to WEB-INF/lib, because you didn't copy them, then go to project properties, deployment, add and add the jars needed for deployment.)

Server
Now, add a class to the project - here I've put it in a package I called example.jersey and named the class Hello. This class is a simple servlet to greet a request. Note the @Path to specify the path off the root in web.xml (shown next), the @GET for the HTTP verb that a method will respond to, and the @Produces for the media type to produce (there's also @Consumes for the media type to expect in the request).

 package example.jersey;  
 import javax.ws.rs.*;  
 import javax.ws.rs.core.MediaType;  
 @Path("/hello")  
 public class Hello {  
      @GET  
      @Produces(MediaType.TEXT_PLAIN)     // nature of MIME type  
      public String simpleStringResponse() {  
           return "hello from the text response of Hello";  
      }  
      @GET  
      @Produces(MediaType.TEXT_HTML) // nature of MIME type  
      public String simpleHTMLResponse() {  
           return "<html> <title> Simple RESTful Hello</title> "  
                     + "<body><h2>Hello from the html response of class Hello :)</h2></body></html>";  
      }  
 }  

Here is the web.xml to update (in WEB-INF).  One noticeable change from Jersey 1.x to 2.0 is the classes to be referenced in servlet-class and param-name:
     org.glassfish.jersey.servlet.ServletContainer 
and 
   jersey.config.server.provider.packages
respectively.
As expected, note the url-pattern path (relates to the url needed to find the resource) and param-value (relates to the class package) as that will be important to checking the result.

 <?xml version="1.0" encoding="UTF-8"?>  
 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">  
  <display-name>SomeAppDisplayName</display-name>  
  <welcome-file-list>  
   <welcome-file>index.html</welcome-file>  
  </welcome-file-list>  
  <servlet>  
  <servlet-name>HelloServlet</servlet-name>  
    <servlet-class>  
     org.glassfish.jersey.servlet.ServletContainer  
   </servlet-class>  
   <init-param>  
     <param-name>jersey.config.server.provider.packages</param-name>  
     <param-value>example.jersey</param-value>  
   </init-param>  
   <load-on-startup>1</load-on-startup>  
 </servlet>  
 <servlet-mapping>  
   <servlet-name>HelloServlet</servlet-name>  
   <url-pattern>/jersey_test/*</url-pattern>  
 </servlet-mapping>  
 </web-app>  
With that configured, right click on the project (assuming in Eclipse still) and click Run As -> Run on Server to launch the app on a configured server (tomcat).  Then go to the url: http://localhost:8080/DynamicWebP/jersey_test/hello - if you use a browser (instead of curl), the response should be:

      Hello from the html response of class Hello :)

Client
Now, add a REST Jersey client by using the same project and the following JerseyClient class in the package client.jersey.

 package client.jersey;  
 import javax.ws.rs.client.*;  
 import org.glassfish.jersey.client.filter.CsrfProtectionFilter;  
 public class JerseyClient {  
       public static void main(String[] args) {  
            Client restClient = ClientBuilder.newClient();  
            //restClient.register(new CsrfProtectionFilter()); //register a filter, here a predefined one  
            WebTarget target = restClient.target("http://jersey.java.net//");  
           // target.register(new CsrfProtectionFilter());//or register on a target  
            WebTarget resourceTarget = target.path("download.html"); //change the URI without affecting a root URI  
            String responseString = resourceTarget.request("text/plain").get(String.class);  
            System.out.println("Here is the response: "+responseString);  
       }  
 }  

Note the separation of higher web targets (urls) from lower ones - you can set filters and paths at various levels.  The commented out code is just to illustrate the setting of filters at two levels.
(More info and examples on the simple setup of a client are here: https://jersey.java.net/documentation/latest/user-guide.html#client.)

To run the example, right click on the JerseyClient.java file in Eclipse and Run As -> Java Application to invoke the main(...) in the class.  Your console output should be the html text from the Jersey download page.

That's it - client and server side. We could make it more exciting by using query parameters and doing a recursive call to each class (you'd need to make some changes) until a value is reached like a predetermined limit (or something crashes :)

Jersey 2 client - no pathParam

I came across the pathParam in the Jersey 2 client documentation the other day and thought it would be useful in various client requests:
https://jersey.java.net/documentation/latest/migration.html#mig-client-simple-request
Specifically, this line in the JAX-RS 2.0 way:
String result = target.pathParam("param", "value").get(String.class);

However, coding it up ran into a problem very quickly - namely that the method wasn't available on a WebTarget class!  Searching the jar confirmed that, but searching the internet didn't turn up much besides this:
https://java.net/projects/jax-rs-spec/lists/jsr339-experts/archive/2012-09/message/22
Which looks like the beginning of the discussion to remove pathParam :)
So, the lesson is: ignore that line in the documentation!

Thursday, July 18, 2013

Fixing the Akka Java tutorial

The first Akka Java tutorial is a nice introduction to Akka actors and messages.  In terms of programming, this method has been around for a long time and the Akka libraries provide a good way for the Java community to use messages and actors.

However, the tutorial in its current state (July 2013) has some problems that will trip up developers and leave you searching for solutions - here are some answers.  As usual, if you know what the problem is, then it's easy to deal with - in fact, the code is fine, but only when mixed with the right Akka.

First, the links to the tutorial in github are wrong - look on this tree (2.0.2). Alternatively, you can cut and paste the program code from the tutorial here

Which version of Akka do you want to run? The tutorial code as is requires an older version of Akka - the tutorial was written to Akka 2.0.2, but also works with Akka 2.0.5 which can be downloaded here or via the download page.  It won't work as is with Akka 2.2.0.

Akka 2.2.0+ - change one line
The most annoying issue that I came across was the error below.  It's a bit easier to fix when using Eclipse (or similar IDE), but I was using a text editor:
tutorial\Pi.java:31: error: method tell in class ActorRef cannot be applied to given types;
     master.tell(new Calculate());
           ^
  required: Object,ActorRef
  found: Calculate
  reason: actual and formal argument lists differ in length 
(compiled with:
 C:\Tools\akka-2.2.0>"c:\Program Files\Java\jdk1.7.0_09\bin\javac.exe" 
          -cp lib\scala-library.jar;lib\akka\akka-actor_2.10-2.2.0.jar tutorial\Pi.java 

The method signature for master.tell() should accept messages only or messages and actor references - except that they were deprecated in Akka 2.1 and removed in Akka 2.2.  Make sure you can find the right documentation.

To fix, add master.noSender() or null to the method call if using an Akka version greater than 2.1. Then recompile and run including the same jars as above.

Second solution - use older version of Akka

For the sake of simplicity, comment out the package statement at the top of the program:
 //package akka.tutorial.first.java;

Now, it's just a matter of compiling and running the code - as easy as it was supposed to be.  Put the tutorial directory in the akka-2.0.5 directory so that it's at the same level as Akka's lib directory.

javac.exe -cp lib/scala-library.jar:lib/akka/akka-actor-2.0.5.jar tutorial/Pi.java 
(for *nix/linux)
javac.exe -cp lib\scala-library.jar;lib\akka\akka-actor-2.0.5.jar tutorial\Pi.java 
(for windows)
or more generically: 
javac.exe -cp path_to_akka/lib/scala-library.jar:path_to_akk/libakka/akka-actor-2.0.5.jar \
    path_to_tutorial/tutorial/Pi.java 
(for linux - similarly for windows)
(compiled with java 7)

Running the example requires just a little more:
java.exe -cp lib\scala-library.jar;lib\akka\akka-actor-2.0.5.jar;tutorial;lib\akka\config-0.3.1.jar Pi 
(for windows, run from the common directory if you've done that)
java.exe -cp lib/scala-library.jar;lib/akka/akka-actor-2.0.5.jar;tutorial;lib/akka/config-0.3.1.jar Pi 
(for linux, *nix, run from the common directory if you've done that)
p_a=path_to_akka #set p_a equal to the path to akka, for example /opt/akka/lib
java.exe -cp \
$p_a/lib/scala-library.jar:$p_a/lib/akka/akka-actor-2.0.5.jar:path_to_tutorial/tutorial:$p_a/lib/akka/config-0.3.1.jar Pi 
(for linux, *nix, specifying exact paths)

Output
When run, the output should look like:
        Pi approximation:               3.1415826535897926
        Calculation time:       1187 milliseconds