2013年12月5日木曜日

Genba is not dead または Genba will never die

この記事はDevLOVE Advent Calendar 2013「現場」 の27日目です。
kimura_m_29さんからバトンを受け取りました。

自己紹介

はじめまして。羽飼康(はがいやすし)と申します。
ソフトウェアの開発を生業としております。主に受託開発のお仕事をさせていただいています。
名古屋アジャイル勉強会、DevLove名古屋のスタッフをさせていただいております。

現場

現場ってなんでしょうねって思って、私が最初にイメージした『現場』はこんな感じです。

  • 工作機械の製造現場
  • 工作機械を使ってアルミなどを削り出している製造現場
  • 物流の現場
  • 土木工事の施工現場
これらは私がお仕事で関わることのあった『現場』です。
特に『土木工事現場』は私にとってとても大切な思いのある『現場』だったりします。
こうやって見てみると建設業だったら工事現場、製造業であれば製造の現場というように、それぞれの組織・業界において、「取引の対象となるモノ(コト)」が産み出されている場所を『現場』と呼んでいる事が多いように思います。我々の業界でも『現場』というと、やはりソフトウェア開発を行っている場をそう呼びますね。
とはいうものの、我々がシステム化対象業務の実態を知るために「現場を見る」とか「現場の声を聞く」とか言って、実際の業務が行われている有様を見させてもらったり、話を聞かせてもらったりします。この場合必ずしも「生産」の場では無い。
営業活動だったり、いわゆる事務であったり、システム運用業務の場合もありました。
実はすべてのコトが行われている場は『現場』なんですね。それぞれの現場があって、ある現場で閉じているモノなど一つも無くて、すべて現場と現場の関わりで成り立っている。システム開発に関わっている我々はそれを知っているはずなのです。
例えば『モノを買う』という行為は、

  • 生産するための材料を入手するというコト
  • 取引先に発注するというコト
  • 仕分けを起票するというコト
  • ウォッチすべき原価に変化が起きるというイベント
という具合に、それぞれの現場・役割においてする事も意味も異なったりします。それでも目指すべき大きなゴールは同じであるはずです。

『現場意識を強く持っているのはおそらく『生産の現場』です。これはその現場の中の人も外の人もそう思っていることが多いと思います。
こういう現場を指す『現場』という言葉は、水戸黄門の印籠のように使われることがあります。
『現場』または『現実』はこうだっ!と言い放つことによって思考停止に陥るアレです。悲しいことに我々の業界でこの印籠が頻繁に使われちゃってないかなぁって思います。
いろんな現場が相互作用して成り立ってるって事を外から見てよく知ってるはずの我々がそれやっちゃってるんですね。それで軋轢が生まれたり壁が出来たりする。
『現場』 of 『現場』でそれやっちゃったらね。
あまりにもったいないので、そういうのはやめたらいいかな。

そんな愚痴は置いておいて、自分関わる現場は前向きに前進させたい。
今年は特にそんな気持ちで仕事に取り組んだつもりです。
勉強会などででもいい刺激をたくさん受けることが出来ました。現場の外での学びを現場に持ち込んだり。私に関わっていただいた皆さんに心より感謝申し上げます。
わりと勢いよくチャレンジしたように思います。うまくいったり苦労したりでした。幸いすばらしいメンバーの力でどうにかなってます。
いい歳して落ち着き無くて楽しかったな。わがままにつきあってくれたメンバーありがとう。
そしていつもの事ながら力不足を痛感したりしました。歩みの遅いおっさんはいつまでも学びがあって退屈しません。
私の現場を外から見てた人は、良いとこだけ持って行ってください。

なんか年末なのでこういう感じになっちゃいました。
来年はもうちょっといろいろうまくやれるかな。

バトン

明日は@shin_semiyaさんです。ガンギマリだそうです。
きっとアッパー系でしょう。


2013年5月25日土曜日

Agile Japan 2013 サテライト<名古屋>

昨日Agile Japan 2013が開催されました。 
名古屋のサテライト会場は『モデリングxアジャイル』テーマで、名古屋アジャイル勉強会が主催で開催。
午後のワークショップは私が担当しました。
YOU&Iさんをはじめとする名古屋アジャイル勉強会のスタッフのみなさん、ドメイン駆動設計読書会@名古屋DDDNagoya) 主催のRKTMさんと参加者のみなさんのサポートによりなんとかできました。
心より感謝申し上げます。


今回は「アジャイルにモデリング」ということで、モデルの書き方ではなく、どういう『姿勢』でモデリングするかという事が主題。
特に、

  • モデルを使ってコミュニケーション
  • インクリメンタルにモデリングする

という2点を意識した内容でいきました。

今回のワークショップの準備で、スコット・W・アンブラーの『アジャイルモデリング』をそれなりにちゃんと読んでたりして、モデリングという行為について良く考えるいい機会になったと思う。
普段の仕事でも、もう少し無駄を省けるかなという気がする。

”モデリング”と言うと、表記法などモデルそのもの書き方が話題の中心となることが多い。
まあそれはとても普通の事なんだけど。
『アジャイルモデリング』は、アジャイルにモデリングに取り組む『姿勢』が語られている本。タイトルに「モデリング」という言葉がはいっていて、これだけ記法などが書かれていない本は他にないんじゃないかな。
今回のワークショップで、参加者の皆さんにその”姿勢”が伝わっていたらとても嬉しいです。


  • 複数人でモデリングするという事
  • やりすぎてわけわかんなくなる前に実装してみるという事

が、結構イイ感じである事は皆さん感じていただけたのかなと思う。

普段の仕事でも、ホワイトボードに簡単にモデルを書いて「こうじゃね?」という感じでディスカッションする事は時折あって、生産性の高いコミュニケーションになっていると思う。
ドメイン駆動設計読書会@名古屋(DDDNagoya) でも、参加者みんなでわいわいと意見を出し合いながらモデリングしたりして、これがまた楽しかったり(私はなかなか参加できていないが…)。
また、Qcon Tokyo 2013でも、原田騎郎さんの『DDD を Scrum で廻す。あるいは Scrum を DDD で廻す。』というセッションがあって、その中で複数人でホワイトボードでやるのが良いと言われていた。
そうする事によって、メンバーのモデリングスキルも上がってゆく。
もっと積極的にこのやり方を使っていきたいと思う。




2013年1月15日火曜日

ExtJS 覚書 #2 validation

Ext.data.Modelを使ったクライアントサイドバリデーション

データモデルにvalidationの定義を含めることができる。
Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: [
        {name: 'name',     type: 'string'},
        {name: 'age',      type: 'int'},
        {name: 'phone',    type: 'string'},
        {name: 'gender',   type: 'string'},
        {name: 'username', type: 'string'},
        {name: 'alive',    type: 'boolean', defaultValue: true}
    ],
    validations: [
        {type: 'presence',  field: 'age'},
        {type: 'length',    field: 'name',     min: 2},
        {type: 'inclusion', field: 'gender',   list: ['Male', 'Female']},
        {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
        {type: 'format',    field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
    ]
});

バリデートはExt.data.Modelのvalidate()で行う。
結果は、Ext.data.Errorsが返される。

var errors = instance.validate();

サーバサイドバリデーション

サーバサイドのバリデーションの結果をExt.data.Model.validate()と同じ形で得るためには、データモ
デルのproxyにreaderを正しく設定する必要がある。
 Ext.define('Employee', {
    extend : 'Ext.data.Model',
    idProperty : 'id',
    fields : [
        { name : 'id', type : 'string' },
        { name : 'firstName', type : 'string' },
        { name : 'lastName', type : 'string' },
        { name : 'gender', type : 'string' }
    ],
    proxy : {
        url : "/コンテキストルート/employeeService",
        type : "rest",
        reader : {
            type : 'json',
            messageProperty : 'errors'
        }
    }
});

このデータモデルのsave()メソッドに対して、サーバ側バリデーションを返す場合、以下のようなJSONデータをサーバは返さなければならない。
{
    'success' : false,
    'errors' : { 'id' : 'Error Message1', 'firstName' : 'Error Message 2', 'lastName' : 'Error Message3' }
}

readerのmessagePropertyの値と、JSONデータのエラーのプロパティを一致させること。
バリデーション結果は、モデルのload(), save(), destroy()メソッドのsuccess, failureコールバックの引数 operation (ext.data.Operation) から得られる。
var emp = Ext.ModelManager.getModel('Employee');
emp.set('123', 'Fidel', 'Castro', 'Male');
emp.save({
    waitMsg : 'Saving...',
    success : function(operation){ },
    failure : function(operation){
        var errors = operation.getError();
    }
});

validationの結果をformに適用する
formのmarkInvalid()を使ってvalidationの結果をformに適用する事が出来る。
var errors = instance.validate();
form.markInvalid(errors);

jax-rs(jersey)覚書 #4 自前のシリアライザー : 日付フォーマットを指定する

jax-rsはオリコウさんで、pojoがそのままjsonになって飛んでいきます。
ところが、java.uti.Dateのフィールドがイケてない形で行ってしまいました。
その結果、Ext JSのグリッドやformのdate型のフィールドに表示されず。
jsonにシリアライズするときにフォーマットの指定できないかしらと調べてみた。

まず、org.codehaus.jackson.JsonGenerator を継承したシリアライザーを作る。
serializeというabstractのメソッドを実装してあげればよい。
Date型を yyyy/MM/DD にするならこんな感じ。

import java.io.IOException;
import java.text.SimpleDateFormat;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

public class MyDateSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator gen,
   SerializerProvider provider) throws IOException,
   JsonProcessingException {

        SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
        String formattedDate = formatter.format(value);

        gen.writeString(formattedDate);
  
    }
}

そして、jsonに変身するクラスの、作ったシリアライザーを適用したいフィールドを @JsonSerialize アノテーションで修飾する
public class Person {
 
    private String firstName;
    private String lastName
    
    @JsonSerialize(using = MyDateSerializer.class)
    private Date birthday;
    

こんな感じでした。

ExtJS覚書 #1 Ext.data.ModelでREST


ExtJSでは、Ext.data.Modelを拡張したデータモデルをを使って、正しくRESTでJSONデータをやり取りする事できる。
データのライフサイクルまでそれなりに管理してくれていて便利ではあるが、何をしてくれるかを正しく理解する必要がある。
ここでは、モデルとフォーム(Ext.form.Panel)を使って一意のデータをCRUDする方法を説明する。

データモデル(Ext.data.Model)とCRUD

id, 姓, 名, 性別 という要素を持つEmployeeというデータをCRUDするRESTのクライアントの定義は以下のようになる。
idPropertyはこのデータモデルのIDとなるプロパティを指定する。
proxyのtypeに"rest"を指定することにより、Ext.data.proxy.Restが使われる。正しくRESTの型にはまる。

Ext.define('Employee', {
    extend : 'Ext.data.Model',
    idProperty : 'id',
    fields : [ 
        { name : 'id', type : 'string' },
        { name : 'firstName', type : 'string' },
        { name : 'lastName', type : 'string' },
        { name : 'gender', type : 'string' }
    ],
    proxy : {
        url : "/コンテキストルート/employeeService",
        type : "rest"
    }
});

データをロードする(loadメソッド)

このデータモデルを使ってデータをロードするコードは以下の通り。
var emp = Ext.ModelManager.getModel('Employee');
emp.load('123', {
        waitMsg : 'Loading...',
        success : function(record, operation){ },
        failure : function(record, operation){ }
        });

これで、 http://ホスト/コンテキストルート/employeeService/123 にGETリクエストが送られる。
success, failure関数の引数recordには、得られたデータ(Employeeのインスタンス)が入る。

データモデルをformにセットする

form(Ext.form.Basic)のloadRecord()を使って、データモデルをformにセットする事が出来る。
この時データモデルの内容で、formの要素は書きかえられる。
データモデルのfieldと、formの要素の名前(と型)が一致していればOK。
var emp = Ext.ModelManager.getModel('Employee');
emp.load('123', {
        waitMsg : 'Loading...',
        success : function(record, operation){
            empForm.getForm().loadRecord(record);
        },
        failure : function(record, operation){ }
        });

データを新規作成する(saveメソッド)

Ext.ModelManager.create()を使ってデータモデルを新規作成する。
save()メソッドでJSONデータをPOSTする。
var emp = Ext.ModelManager.create('Employee',{id : '123', firstName : 'Fidel', lastName : 'Castro', genger: 'male'});
emp.save({
        waitMsg : 'Saving...',
        success : function(record, operation){ },
        failure : function(record, operation){ }
        });

これで、 http://ホストコンテキストルート/employeeService に、JSONデータがPOSTされる。
success,failure時の関数の引数 record はにデータモデルのインスタンス、operationには、ext.data.Operationのインスタンスがセットされる。
ここで、setId()メソッドは使ってはならない。この件については『データを更新する』のセクションで説明する。
成功時、operationのconfig、"action"には"create"が入っている。

formのデータを使って新規作成する

フォームのデータを使って作る場合はExt.ModelManager.create()の第2引数をform.getValues()にすればよい。
formの要素と、データモデルのフィールドの名前と型は合わせておく。
var form = this.up('form').getForm();
var emp = Ext.ModelManager.create('Employee', empForm.getForm().getValues(false));
emp.save({
        waitMsg : 'Saving...',
        success : function(record, operation){
            empForm.getForm().loadRecord(record);
        },
        failure : function(record, operation){ }
        });

データを更新する(saveメソッド)

load()されたModelでsave()メソッドを使うとJSONデータがPUTされる。
この時のURLはproxyで設定したURLに"/id文字列"が付いたものになる。
上記の例で、idPropertyに設定したフィールド(デフォルトは"id")の値が"123"であった場合、
http://ホスト/コンテキストルート/employeeService/123
となる。
新規作成もsave()メソッドを使う、save()メソッドによってPOST(create)されるのかPUT(update)されるのかは、phantomというプロパティがtrueかどうかで決まる。trueならPOSTとなる。
このphantomというプロパティは、Modelのidプロパティ(デフォルトはid)に値が入っているかどうかで決まる。
model.setId(値);を実行するとphantomがfalseにセットされるようになっている。
どうもExt.data.Modelは、

  • idは一つ
  • idは新規作成時に付与される

というポリシーで作られている。
複合キーであったり、id(コード等)をユーザが決める場合はこの事を意識してプログラミングする必要がある。


formからデータモデルのインスタンスを取り出してPUTする

form(Ext.form.Basic)のgetRecord()を使って、formからModelのインスタンスを得る事が出来る。form.loadRecord()によってセットされているModelが得られる。
フォームにModelがロードされていない状態(loadRecord()されていないform)では、getRecord()はundefinedを返す。
ただし、ここで得られたrecordは、フォーム上での編集が反映されていない(編集前のデータが残っているということ)。
form(Ext.form.Basic)のupdateRecord()で、現在のフォームのデータを反映する事が出来る。

var emp = form.getRecord();    /* フォームよりデータモデルのインスタンスを得る */
form.updateRecord(emp);        /* 現在のフォーム上のデータでモデルの内容を更新する */
emp.save({
        waitMsg : 'Saving...',
        success : function(record, operation){ },
        failure : function(record, operation){ }
        });

成功時、operationのconfig、"action"には"update"が入っている。

データを削除する(destroy()メソッド) 

削除するコードは以下の通り。
var emp = Ext.ModelManager.getModel('Employee');
emp.set('123', 'Fidel', 'Castro', 'Male');
emp.destroy({
        waitMsg : 'Deleting...',
        success : function(operation){ },
        failure : function(operation){ }
        });

これで、 http://ホスト/コンテキストルート/employeeService/123 にDELETEリクエストが送られる。
saveと同様に、JSONデータが送られる。
123の部分には、データモデル定義のidPropertyに指定したフィールドの値がセットされる。

jax-rs(jersey)覚書 #3 bean validation


これはjax-rsというわけではないが、セットで使うことが多いと思う。

JSR303 bean validationで、文字列の長さ、数値の大きさ、日付、nullなどの値チェックはほとんど可能。
使い方は非常に簡単で、validationしたいクラスのフィールドにアノテーションを設定すればよい。
javax.validation.constraintsには以下のアノテーションが定義されている。

  • @AssertFalse,@AssertTrue
  • @DecimalMax,@DecimalMin
  • @Max,@Min
  • @Digits
  • @Future,@Past
  • @Null,@NotNull
  • @Pattern
  • @Size

文字列の長さをチェックするには以下のようにする。
public class Employee {
    @Size(max=3, message="3桁までですよ!")
    private String empId;
    private String firstName;
    private String LastName;
    private String gender;

validationは、インターフェース javax.validation.Validator を使う。
SpringにDIさせるにはbean定義ファイルに以下の設定を行う。

;

実際にvalidationする例は以下の通り

@Service
public class EmployeeService {
    @Autowired
    private Validator validator;
    
    private void validateEmployee(Employee emp) {
        Set<Constraintviolation<Employee>> violations = validator.validate(emp);
    
        for (ConstraintViolation<employee> violation : violations) {
             System.out.print(violation.getPropertyPath().toString());
             System.out.print(" : ");
             System.out.println(violation.getMessage());
        }
    }

javax.validation.Validator.validate()で、javax.validation.ConstraintViolationのSetが得られる。
ConstraintViolation.getPropertyPath().toString()で、フィールド名が得られる。
ConstraintViolation.getMessage()で、アノテーションで設定したメッセージが得られる。

メッセージをプロパティファイルから得ることもできる(そうすべき)。
デフォルトのプロパティファイル名はValidationMessages.properties 。
public class Employee {
    @Size(max=3, message="{emp.id.size}")
    private String empId;

jax-rs(jersey)覚書 #2 基本編


サービスのパスの設定

@Pathアノテーション

クラス、メソッドに指定して、RESTサービスのパス(URL)を決める。
@Path("employee")
public class EmpService{
    
    @GET
    @Path("service1")
    public Response service1() {
この場合サービスのURLは
http://ホスト/コンテキストルート/employee/service1
となる。

可変パスの扱い
@Pathと@PathParam

RESTでは”一意なURL”というのがが基本的な考え方としてある。URLのパスにIDを含める形になる(”サービス/{id} ”というような形)。
jax-rsではパスの値が可変なURLを扱うことができる。
@Path("employee")
public class EmpService{
    
    @GET
    @Path("{id}")
    public Response service1(@PathParam String id) {
これで、
http://ホスト/コンテキストルート/employee/123
にアクセスした場合、 @PathParamアノテーションで修飾した引数 id に"123"が入ってくる。

複合キーならどうするか?

複合キーを"-"等で繋いだ場合、は以下のような形になる。
http://ホスト/コンテキストルート/employee/123-111 の場合

@Path("employee")
public class EmpService{
    
    @GET
    @Path("{id1}-{id2}")
    public Response service1(@PathParam String id1, @PathParam Strind id2) {


HTTPメソッドの指定

@GET, @PUT, @POST, @DELETE, @HEADアノテーションを使って、どのHTTPメソッドで受けるかを指定する。
同一パスでもHTTPメソッドによって処理を切り分けることができる。
@Path("employee")
public class EmpService{
    
    @GET
    @Path("{id}")
    public Response get(@PathParam String id) {
        
    }
    
    @PUT
    @Path("{id}")
    public Response update(@PathParam String id) {
        
    }
    
    @POST
    @Path("{id}")
    public Response insert(@PathParam String id) {
        
    }
    
    @DELETE
    @Path("{id}")
    public Response delete(@PathParam String id) {
        
    }
}
この場合、同じURL
http://ホスト/コンテキストルート/employee/123
を、HTTPメソッドを分けて使うことができる。正しいRESTの形はこのように実現できる。
実装されていないメソッドでアクセスがあった場合、405 Method Not Allowed がクライアントに返される。

POST,PUTされる様々なContent-TypeのデータをJavaオブジェクトへ変換する

@Consumesアノテーションで受け取るデータのContent-Typeを指定することができる。
たとえば以下のJSONデータをPOSTされて、Employeeというクラス(Value Object)にマッピングする場合は以下のようになる。

{ "empId" : "123", "firstName" : "Oscar" , "lastName" : "Jarjays", "gender" : "female" }

@Path("employee")
public class EmpService{

 @POST
 @Consumes({ MediaType.APPLICATION_JSON })
 public Response post(Employee employee) {

public class Employee {

    private String empId;
    private String firstName;
    private String LastName;
    private String gender;
    
    public String getEmpId() {
        return empId;
    }
    public void setEmpId(String empId) {
        this.empId = empId;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return LastName;
    }
    public void setLastName(String lastName) {
        LastName = lastName;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }

}
JSON以外のContent-Typeにも対応している。

  • application/json : MediaType.APPLICATION_JSON
  • application/xml : MediaType.APPLICATION_XML
  • application/x-www-form-urlencoded : MediaType.APPLICATION_FORM_URLENCODED
  • application/octet-stream : MediaType.APPLICATION_OCTET_STREAM
  • multipart/form-data : MediaType.MULTIPART_FORM_DATA
  • text/plain : MediaType.TEXT_PLAIN
  • etc..

Javaオブジェクトを様々なContent-Typeに変換してレスポンスを返す


@Producesアノテーションでレスポンスとして返すContent-Typeを指定する。
javax.ws.rs.core.Responseを使って、実際に変換を行ってレスポンスを返す。
EmployeeをJSONにして返す場合は以下のようになる。
@Path("myRestService")
public class MyRestService {
    
    @GET
    @Path("{id}")
    @Produces({ MediaType.APPLICATION_JSON })
    public Response get(@PathParam(name = "id") String id) {
        
        Employee emp = getEmp(id);
        return Response.ok(emp, MediaType.APPLICATION_JSON).build();
    }

Response.ok()ではHTTPステータスコード200を返す。

OK以外のレスポンスを返す
javax.ws.rs.core.Response.status() を使う。


  • status(int status)
  • status(Response.Status status)
  • status(Response.StatusType status)


  • Response.status(404);
  • Response.status(500);
  • Response.status(Status.FORBIDDEN);
  • Response.status(Status.INTERNAL_SERVER_ERROR);


クエリパラメータを扱う

http://ホスト/コンテキストルート/myRestService?param1=value1&param2=value2
という形で、パラメータを指定した場合は、引数を@QueryParamアノテーションで修飾することによって受け取ることができる。
@Path("myRestService")
public class MyRestService {
    
    @GET
    public Response get(@QueryParam(value="param1") param1, @QueryParam(value="param2" param2)) {

この方法だと、パラメータ値一つ一つをバラバラに受け取ることになる。
クラスにマッピングする方法もある。

まず、パラメータを受け取るクラスのフィールドに@QueryParamアノテーションをつける。
public class Employee {

    @QueryParam("id")
    String id;
    
    @QueryParam("firstName")
    String firstName;

    @QueryParam("lastName");
    String lastName;

サービスのメソッドの引数に@Contextアノテーションをつけたcom.sun.jersey.api.core.ResourceContextを指定する。
ResourceContext.getResource()にパラメータを格納するクラスを指定して、インスタンスを得ることができる。
これは@QueryParamだけでなく、@PathParam等にも使える。

@Path("myRestService")
public class MyRestService {
    
    @GET
    public Response get(@Context ResourceContext rc)) {
        
        Employee emp = rc.getResource(Employee.class);
このやり方の問題は、引数がResourceContextになってしまって、欲しいパラメータはResourceContextから得ることになってUnit Testがやりにくくなってしまう。
まとまった数のパラメータをPOJOで受け取りたいのであれば、POSTを使ってPOJOで受け取る方が良さそう。

jax-rs(jersey)覚書 #1 pom.xmlとweb.xml


jax-rs(jersey)あたりのネタはすでに色々落ちてるのですが、せっかくなので書いておくことにします。

mavenのdependency


  • group id: com.sun.jersey
  • artifact id:  jersey-core, jersey-server, jersey-json, jersey-grizzly2, jersey-spring
  • version: 1.16 (2013/01/07時点)

mavenでjersey-springの1.16を指定するとspring3.0のモノが落ちてくる。Springは3.1を使うのでexcludeしている。


    com.sun.jersey
    jersey-core
    1.16


    com.sun.jersey
    jersey-server
    1.16


    com.sun.jersey
    jersey-json
    1.16


    com.sun.jersey
    jersey-grizzly2
    1.16


    com.sun.jersey.contribs
    jersey-spring
    1.16
    
        
            org.springframework
            spring
        
        
            org.springframework
            spring-core
        
        
            org.springframework
            spring-web
        
        
            org.springframework
            spring-beans
        
        
            org.springframework
            spring-context
        
    



servletの設定(web.xml)

init-paramでcom.sun.jersey.api.json.POJOMappingFeatureをtrueに設定しないと、JSON(XML)とPOJOとのマッピングがされないので注意。
com.sun.jersey.config.property.packagesで、RESTサービスになるクラスのパッケージを指定する。


    Jersey REST Service
    com.sun.jersey.spi.spring.container.servlet.SpringServlet
    
        com.sun.jersey.api.json.POJOMappingFeature
        true
    
    
        com.sun.jersey.config.property.resorceConfigClass
        com.sun.jersey.api.core.PackagesResourceConfig
    
    
        com.sun.jersey.config.property.packages
        jp.onestepbeyond.ckndemo0.service.rest
    
    1

    

    Jersey REST Service
    /rest/*