ひこぽんのーと

覚書と雑記です。

JavaEE 7をやってみよう。 JAX-RS その3

その2のつづき。

今度はリクエストにパラメータを持たせたパターン……つまり、POSTでの実装を試してみる。
加えて、リクエストはJSONでレスポンスがXMLのパターンをやってみる。
JSONJAVA変換も、JAVAXML変換もJAXBという機能を使って行うが、
ランタイムが勝手にやってくれるので、ここでは難しく構える必要はない。

とはいっても、Javaのコードを書く前に
レスポンスのXMLがどんな構造をもつのか位は決める必要があるので、
簡単に下のようなものを考えてみた。

<result>
    <warShipList>
        <warShip>
            <type>軽巡洋艦</type>
            <name>球磨</name>
        </warShip>
        <warShip>
            <type>軽巡洋艦</type>
            <name>多摩</name>
        </warShip>
    </warShipList>
</result>

warShipListにwarShipの情報が0個〜n個で入っている。
warShipにはtypeとnameの要素を持つ、そんな感じ。

これを踏まえて、まず、Beanクラスを作る。

WarShipクラス
typeとnameを持つ艦艇クラス。
@XmlType(propOrder={"type", "name"})は、XML出力する際のタグの出力順を表す。
このアノテーションは無くてもXML出力はできるが、
項目名のアルファベット順になってしまうのが嫌なので(型 → 名前の順でしょう、やはり)、追加した。
@XmlElementは重要なアノテーションでこれを付けた項目がXMLの要素として出力される。
メンバに付けるとわかりやすいのだが、メンバはprivateなのでこの例では付けられない。
その代わりアクセサメソッドのgetterにアノテーションを付けている。
package jaxrs.bean;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;

@XmlType(propOrder={"type", "name"})
public class WarShip {
    private String type;
    private String name;
    /**
     * typeを取得します。
     * @return type
     */
    @XmlElement
    public String getType() {
        return type;
    }
    /**
     * typeを設定します。
     * @param type type
     */
    public void setType(String type) {
        this.type = type;
    }
    /**
     * nameを取得します。
     * @return name
     */
    @XmlElement
    public String getName() {
        return name;
    }
    /**
     * nameを設定します。
     * @param name name
     */
    public void setName(String name) {
        this.name = name;
    }
}
Catalogクラス
XMLのタグ名がResultだったけど、Resultクラスとするのもアレなので、カタログということにした。
@XmlRootElementアノテーションが重要で、このアノテーションを持つクラスがJavaクラスからXMLへ変換できる。
名称の通り、このクラスがルートタグとなる。
@XmlRootElementアノテーションのパラメータ"name"があるが、これはルートタグの名称を指定する意味がある。
省略は可能で、省略するとクラス名がタグ名となる。
package jaxrs.bean;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="result")
public class Catalog {
    List<WarShip> shipList;
    
    @XmlElement
    public List<WarShip> getShipList() {
        return shipList;
    }
    
    public void setShipList(List<WarShip> shipList) {
        this.shipList = shipList;
    }
}

これでアウトプットは揃ったので、処理をリソースクラスに追加する。

リソースクラス
まず、メソッドに@POSTアノテーションを付けてPOST送信された場合に動作するように宣言した。
@Consumesアノテーションで受け入れ可能なパラメータのコンテントタイプを指定している。
リクエストデータはJSONとしたので、"application/json"となっている。
@Producesは前回書いたようにレスポンスのコンテントタイプ。
XMLで返したいので"application/xml"。
@FormattedアノテーションJBOSS(Wildfly)でしか使えないアノテーションなのだが、これを付けるとXMLの行端に改行が付加されて見やすい形で返されるので付けてみた。
メソッドの戻り値に@XmlRootElementが付いているクラスを使うことで自動的にJavaXML変換が行われる。
インプットについても同じ理屈でJSONJava変換が行われる。
ただし、こちらはJSONなので@XmlRootElementなどのアノテーションは不要。
構造が合っていれば変換してもらえる。
package jaxrs;

import java.util.ArrayList;
import java.util.List;

import javax.enterprise.context.RequestScoped;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import jaxrs.bean.Catalog;
import jaxrs.bean.WarShip;
import jaxrs.bean.WarShipList;

import org.apache.commons.lang.StringUtils;
import org.jboss.resteasy.annotations.providers.jaxb.Formatted;

@RequestScoped
@Path("ship")
public class ShipResource {

    〜〜 前回追加分は省略 〜〜

    @POST
    @Consumes({"application/json;charset=UTF-8"})
    @Produces({"application/xml;charset=UTF-8"})
    @Path("/list")
    @Formatted
    public Catalog getShipListJson(WarShip args) {
        Catalog cat = new Catalog();
        List<WarShip> shipList = new ArrayList<WarShip>();
        if (StringUtils.equals("D", args.getType())) {
            shipList.add(getWarShip("駆逐艦", "睦月"));
            shipList.add(getWarShip("駆逐艦", "如月"));
            shipList.add(getWarShip("駆逐艦", "皐月"));
            shipList.add(getWarShip("駆逐艦", "文月"));
            shipList.add(getWarShip("駆逐艦", "三日月"));
        } else {
            shipList.add(getWarShip("軽巡洋艦", "球磨"));
            shipList.add(getWarShip("軽巡洋艦", "多摩"));
            shipList.add(getWarShip("軽巡洋艦", "北上"));
            shipList.add(getWarShip("軽巡洋艦", "大井"));
            shipList.add(getWarShip("軽巡洋艦", "木曽"));
        }
        cat.setShipList(shipList);
        return cat;
    }
    
    private WarShip getWarShip(String type, String name) {
        WarShip ship = new WarShip();
        ship.setName(name);
        ship.setType(type);
        return ship;
    }
}

今回、簡単にテスト実行するためにJMeterを使ってみた。
URLは「http://localhost:8080/アプリケーション名/rest/ship/list」。
リクエストパラメータにtype = "D"をJSONで送ることにする。
そのため、Post Bodyはもちろんのこと、
Httpヘッダにも「content-type: appliction/json」を指定する。

f:id:nagamitsu1976:20150810113410p:plain
f:id:nagamitsu1976:20150810114412p:plain

サーバーを起動して実行してみるとなんぞや、XMLは出力されているが形が想定通りに出ていない。

f:id:nagamitsu1976:20150810114424p:plain
warShipのタグがなく、warShipListのタグがそのまま複数個出力されてしまった。
なんだろう、これ。

といったところで、その4へつづく。