Tag Archives: Jersey

Upgrading to Jersey 2.x in Tomcat8

Jersey 1.x is still supported. As of this writing, version 1.19.3 was just released on Oct 24th, 2016.

But recently I discovered that Tomcat8 doesn’t play well with Jersey 1.x so we simply have to upgrade to Jersey 2.x. Easy right? (If it were, I wouldn’t be writing this post.)

Jersey 1.x
Let’s review the Jersey 1.x configurations first

As far as maven dependencies, here’s what I used in my pom.xml

<dependency>
  <groupId>com.cedarsoft.rest</groupId>
  <artifactId>jersey</artifactId>
  <version>1.0.0</version>
</dependency>

<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-json</artifactId>
  <version>1.5</version>
</dependency>

com.cedarsoft.rest:jersey is a bundle that includes these dependencies.

Unfortunately, I could find no such bundle for Jersey 2.x so I had to mix and match until trial-and-error led me to a workable solution. (I save you the trouble.)

Here’s my old web.xml

<web-app version="2.4"
 xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <servlet>
    <servlet-name>JerseyREST</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>INSERT_MY_PACKAGES_AND_CLASSES_HERE</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>JerseyREST</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Jersey 2.x
Now here are the changes for Jersey 2.

   <dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>javax.servlet-api</artifactId>
     <version>3.1.0</version>
     <scope>provided</scope>
   </dependency>
   <dependency>
     <groupId>org.glassfish.jersey.containers</groupId>
     <artifactId>jersey-container-servlet-core</artifactId>
     <version>2.13</version>
   </dependency>
   <dependency>
     <groupId>org.glassfish.jersey.containers</groupId>
     <artifactId>jersey-container-servlet</artifactId>
     <version>2.13</version>
   </dependency>
   <dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-databind</artifactId>
     <version>2.8.5</version>
   </dependency>
   <dependency>
     <groupId>org.glassfish.jersey.media</groupId>
     <artifactId>jersey-media-json-jackson</artifactId>
     <version>2.13</version>
   </dependency>
   <!-- jersey file upload dependencies -->
   <dependency>
     <groupId>org.glassfish.jersey.media</groupId>
     <artifactId>jersey-media-multipart</artifactId>
     <version>2.13</version>
   </dependency>

org.glassfish.jersey.media:jersey-media-multipart is only required for file upload capability.

Here’s the new web.xml

<web-app version="3.1"
         metadata-complete="false"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
  <servlet>
    <servlet-name>JerseyREST</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
    </init-param>        
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>
        INSERT_MY_PACKAGES_AND_CLASSES_HERE
      </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
      <servlet-name>JerseyREST</servlet-name>
      <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Notice, the new servlet class.
It’s also important to load the org.glassfish.jersey.media.multipart.MultiPartFeature to support file upload capability.

You might also have to change some of your code.
For example, all your @JsonIgnore annotation classes will have to change from
org.codehaus.jackson.annotate.JsonIgnore
to
import com.fasterxml.jackson.annotation.JsonIgnore

Tagged ,

Receiving arrays from form elements with Jersey

You may have worked with Jersey before, and for the most part, writing a REST API has been as easy as writing a java API. So to accept an array of Strings, you could do something like this.

Jersey Code:

@Path("/rest/")
public class ServiceClass {

@GET
@Path("/strings")
public Response postStringArray(@QueryParam("keywords") List<String> inputList) {
...
}

HTTP Request:

GET http://example.com/rest/strings?keywords=Hello&keywords=World&keywords=...

And that would work fine. But what if you had to send an array from a form element (because you need to upload a file for example).

Suppose you had this form:

<form enctype="multipart/form-data" method="post" action="/rest/upload">
    Keyword: <input name="keywords" />
    Keyword: <input name="keywords" />
    Keyword: <input name="keywords" />

    File:
    <input type="file" name="file">
    <input type="submit" value="upload">
</form>

Then your Jersey API signature will have to consume MediaType.MULTIPART_FORM_DATA. So you’d think something like this will work

@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndKeywords(
    @FormDataParam("keywords") List<String> keywords,
    @FormDataParam("file") InputStream file_in,
    @FormDataParam("file") FormDataContentDisposition fileDetail) {
  ...
}

But you’ll run into a “wrong number of arguments” exception.

Instead, you’ll need to change the List declaration to something unintuitive like this

@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndKeywords(
    @FormDataParam("keywords") List<FormDataBodyPart> keywords,
    @FormDataParam("file") InputStream file_in,
    @FormDataParam("file") FormDataContentDisposition fileDetail) {

      for(FormDataBodyPart keyword : keywords)
        System.out.println( keyword.getValueAs(String.class) );

}

I should note one last thing. If you’re posting the form attributes via jQuery, you’ll want to take advantage of the FormData object. But you should take care to post the array of keywords correctly.

For example, this is the wrong way of posting it

var formData = new FormData();
var keywords = [];
$('select[name="keywords"]').each(function() {
  if( $(this).val() )
    keywords.push($(this).val());
});
formData.append('keywords', keywords);
formData.append('file', $('input[name="file"]').get(0).files[0]);

And this would be the right way:

var formData = new FormData();
$('select[name="keywords"]').each(function() {
  if( $(this).val() )
    formData.append('keywords',$(this).val());
});
formData.append('file', $('input[name="file"]').get(0).files[0]);
Tagged ,

I’m not going to go over all the reasons why you should have a RESTful service with javascript front-end (e.g. clean separation of front- and back-end, reusable web service components, etc). Here I’ll just show how you can quickly set up a RESTful web service in Java and a javascript/jQuery front-end to access it.

The server part requires just 4 things:
1) Add Jersey dependency
2) Write a java bean and annotate it
3) Write a Service class
4) Deploy the servlet in web.xml

1) Add Jersey dependency

If you’re using Maven, then add the following Jersey (JAX-RS implementation) dependency to your pom.xml

<dependency>
    <groupId>com.cedarsoft.rest</groupId>
    <artifactId>jersey</artifactId>
    <version>1.0.0</version>
    <scope>compile</scope>
</dependency>


2) Write a java bean and annotate it

Create a java bean with the following:
1) annotate the class with @XmlRootElement
2) make sure there’s a null constructor (no arguments)
3) add public setters/getters

import javax.xml.bind.annotation.XmlRootElement
@XmlRootElement
public class Foo {
    private String name;
    public Foo() { }
    public String getName() { return name; }
    public void setName(String s) { name = s; }
}

3) Write a Service class

package com.kodingnotes.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path ("/foo")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class FooService {

    @GET
    @Path("/read")
    public Foo getFoo(@QueryParam("input") String input) {
        Foo foo = new Foo();
        foo.setName(input);
        return foo;
    }
}

4) Deploy the servlet in web.xml

In your web.xml, add the following servlet


<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <!-- ##### REFER TO THE PACKAGE WHERE YOU WANT JERSEY TO PICK UP THE RESOURCE CLASSES ###### -->
    <!-- ##### Use semi-colon to specify multiple packages ###### -->
    <param-value>com.kodingnotes.services</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
    <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
    <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.config.feature.logging.DisableEntitylogging</param-name>
    <param-value>false</param-value>
  </init-param>
  <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>JerseyREST</servlet-name>
  <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

 Edit the <param-value> of the <param-name>com.sun.jersey.config.property.packages<param-name>

A simple web architecture. Part I: The Server

Tagged , , , ,