JerseyでのJSONサービス

JerseyでのJSONサービスの作り方に関して、
Jerseyでレスポンスとして、JSONを戻す場合、JerseyのJSONライブラリとJAXBを使います。
例えば、下のようなJSONを戻すと場合は、JAXBのクラスに構造をマッピングします。

{'category' : [{'key' : '1000',  'value' : 'AAAAA'}, {'key' : '2000',  'value' : 'BBBBB'}]}

JAXBのクラスの作成方法は、下記に記載した方法で、作成します。
JAXBに関して - クロノスの技術系ブログ
また、今回の場合、Apache Mavenのpom.xmlは、下記のようにライブラリを指定しています。

<dependency>
	<groupId>com.sun.jersey</groupId>
	<artifactId>jersey-server</artifactId>
	<version>1.18</version>
</dependency>
<dependency>
	<groupId>com.sun.jersey</groupId>
	<artifactId>jersey-core</artifactId>
	<version>1.18</version>
</dependency>
<dependency>
	<groupId>com.sun.jersey</groupId>
	<artifactId>jersey-json</artifactId>
	<version>1.18</version>
</dependency>
<dependency>
	<groupId>com.sun.jersey</groupId>
	<artifactId>jersey-bundle</artifactId>
	<version>1.18</version>
</dependency>
<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-xjc</artifactId>
	<version>2.2.7</version>
</dependency>
<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-impl</artifactId>
	<version>2.2.7</version>
</dependency>
<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-osgi</artifactId>
	<version>2.2.7</version>
</dependency>

また、JAXBのクラスは、以前、作成したものを使います。

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"category"})
@XmlRootElement(name = "categoryResouce")
public class CategoryResouce {
    protected List<CategoryList.Category> category;
    public List<CategoryList.Category> getCategory() {
        if (category == null) {
            category = new ArrayList<CategoryList.Category>();
        }
        return this.category;
    }
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {"key","value"})
    public static class Category {
        protected String key;
        protected String value;
        public String getKey() {
            return key;
        }
        public void setKey(String value) {
            this.key = value;
        }
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
    }
}

サービスクラスは、通常と同じく普通のクラスを作成し、Jerseyのアノテーションの@Pathを付加します。
作成したクラスへアクセスするURLがhttp://localhost:8080/rest/service/〜だとすると、
クラス名の上に、@Path("service")と下の様に定義することになります。

@Path("/service")
public class JsonService {
}

次にメソッドを定義します。
CategoryResouceを取得するメソッドのgetメソッドを作成する場合、
Jerseyのアノテーションの@GETと@Pathをメソッドの前に付加します。
@GETは、HTTPのGETでアクセスされることを意味し、@Pathで、URLの指定をします。
http://localhost:8080/rest/service/getで、そのメソッドへアクセスさせたいなら、
@Path("/get")と定義します。
実際の実装は、下の様な実装になります。

@Path("/service")
public class JsonService {
   @GET
   @Path("/get")
   public  CategoryResouce get()  {
      CategoryResouce resouce = ObjectFactory().createCategoryResouce();
      List<CategoryList.Category> categoryList = resouce.getCategory();
      CategoryList.Category category1 = new CategoryList.Category();
      category1.setKey("1000");
      category1.setValue("AAAAA");
      categoryList.add(category1);
      CategoryList.Category category2 = new CategoryList.Category();
      category2.setKey("1000");
      category2.setValue("AAAAA");
      categoryList.add(category2);
      return resource;
   }

また、JSONPにも対応したい場合は、 JSONWithPaddingを使う必要があり、
JAXBとcallback名を JSONWithPaddingクラスで、ラップします。

@Path("/service")
public class JsonService {
   @GET
   @Path("/get")
   public JSONWithPadding get(
      @QueryParam("callback") @DefaultValue("callback") String callback)  {
      CategoryResouce resouce = ObjectFactory().createCategoryResouce();
      List<CategoryList.Category> categoryList = resouce.getCategory();
      CategoryList.Category category1 = new CategoryList.Category();
      category1.setKey("1000");
      category1.setValue("AAAAA");
      categoryList.add(category1);
      CategoryList.Category category2 = new CategoryList.Category();
      category2.setKey("1000");
      category2.setValue("AAAAA");
      categoryList.add(category2);
      return new JSONWithPadding(resource, callback);
   }

これで、JSONJSONP両方に対応することができます。
ちなみに、リクエストに対して、どうちらになるかは、リクエストヘッダにより自動的に決まります。
なので、下のリクエストヘッダの場合、JSON形式で、レスポンスが戻ります。

Accept : application/json

JSONPは、下のリクエストヘッダだったら、JSONP形式で、レスポンスが戻ります。

Accept : application/javascript

それと、JSONPですが、ブラウザにより、うまくいかない場合があります。
それは、ブラウザが、クロスドメインでのアクセスに制限が掛かっているものがあり、
Access-Control-Allow-Originで、クロスドメインでのアクセスが許されていないことがあるので、
その時は、レスポンスヘッダにAccess-Control-Allow-Originを付加する必要があります。
その場合のアクセスするメソッドにHttpServletResponseを引数として、渡す必要があり、
Jerseyのアノテーションの@Contextを付加して、HttpServletResponseを渡せるようにします。

@Path("/service")
public class JsonService {
   @GET
   @Path("/get")
   public JSONWithPadding get(
      @QueryParam("callback") @DefaultValue("callback") String callback,
      @Context HttpServletResponse response)  {
      response.addHeader(Access-Control-Allow-Origin, "*");
      .....
   }
}

それで、下の様な設定の意味は、Access-Control-Allow-Originに*を設定することで、
無条件で、許可される設定になります。

response.addHeader(Access-Control-Allow-Origin, "*");

あとは、アプリケーションの web.xml ファイルにサーブレット・ディスパッチャー
を下のに定義して完了です。

<servlet>
  <servlet-name>
     Jersey Service
  </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>chronos.service.resouce</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>Jersey Service</servlet-name>
  <url-pattern>/rest/*</url-pattern>
</servlet-mapping>