 SampleCxf.zip サンプルアプリ (Netbeans/Maven)
SampleCxf.zip サンプルアプリ (Netbeans/Maven)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>SampleCxf</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>SampleCxf</name>
    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <netbeans.hint.deploy.server>Tomcat</netbeans.hint.deploy.server>
    </properties>
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.2.0.RELEASE</version>
        </dependency>    
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-jaxrs</artifactId>
            <version>1.9.11</version>
        </dependency>
        <dependency>
            <groupId>com.ibm.icu</groupId>
            <artifactId>icu4j</artifactId>
            <version>50.1.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.1.1</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.1</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>6.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
/rest/* を CXFServlet に割り付ける
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <display-name>JAX-RS Simple Service</display-name>
    <description>JAX-RS Simple Service</description>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/beans.xml</param-value>
    </context-param>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet>
        <servlet-name>ServletAdaptor</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/webresources/*</url-pattern>
    </servlet-mapping>
</web-app>
Webサービスのインタフェースを実現する POJO を設定。
Provider については、後述 → JSONPを返す
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <context:property-placeholder/>
    <context:annotation-config/>
    <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer"/>
    <bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"/>
    <jaxrs:server id="services" address="/">
        <jaxrs:serviceBeans>
            <bean class="com.mycompany.samplecxf.HelloWorld" />
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <!-- <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/> -->
            <bean class="com.mycompany.samplecxf.MyJsonpProvider" />
        </jaxrs:providers>
    </jaxrs:server>
</beans>
ICU4J を使った時刻形式の変換。HTTP GET (URLパラメータ) でパラメータを受け取って JSON、XML、CSV 形式で返却する。詳細は後述 → JSONを返す、XMLを返す、TEXTを返す、JSONPを返す
package com.mycompany.samplecxf;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.ChineseCalendar;
import com.ibm.icu.util.JapaneseCalendar;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
@Path("/timeconv")
public class HelloWorld {
    @GET
    @Produces("application/json")
    public Response conv(@QueryParam("time") String time) {
        return jsonJst(time);
    }
    @GET
    @Path("/json/jst/{jst}")
    @Produces("application/json")
    public Response jsonJst(@PathParam("jst") String jst) {
        try {
            return Response.ok(timeconv(jst, +9)).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Response.serverError().build();
        }
    }
    @GET
    @Path("/xml/jst/{jst}")
    @Produces("application/xml")
    public Response xmlJst(@PathParam("jst") String jst) {
        try {
            return Response.ok(timeconv(jst, +9)).build();
        } catch (Exception e) {
            e.printStackTrace();
            return Response.serverError().build();
        }
    }
    @GET
    @Path("/csv/jst/{jst}")
    @Produces("text/plain")
    public String csvJst(@PathParam("jst") String jst) {
        try {
            return timeconv(jst, +9).toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "failed";
        }
    }
    private JsonBean timeconv(String timeString, int diff) throws Exception {
        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        Date date = df.parse(timeString);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.HOUR, -1 * diff);
        Date utcDate = cal.getTime();
        Calendar jpCal = Calendar.getInstance();
        jpCal.setTime(utcDate);
        jpCal.add(Calendar.HOUR, +9);
        Date jpDate = jpCal.getTime();
        JsonBean res = new JsonBean();
        // 日本時間
        res.setJst(new com.ibm.icu.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jpDate));
        // 和暦
        JapaneseCalendar jc = new JapaneseCalendar();
        DateFormat jdf = new SimpleDateFormat("Gyy年MM月dd日", new DateFormatSymbols(jc, Locale.JAPANESE));
        jdf.setCalendar(jc);
        res.setJp(jdf.format(jpDate));
        // 干支
        ChineseCalendar cc = new ChineseCalendar();
        DateFormat cdf = new SimpleDateFormat("U", new DateFormatSymbols(cc, Locale.CHINESE));
        cdf.setCalendar(cc);
        res.setCycric(cdf.format(jpDate));
        // GMT
        res.setGmt(new com.ibm.icu.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(utcDate));
        // Julian Day
        res.setJd(new com.ibm.icu.text.SimpleDateFormat("g").format(utcDate));
        // MJD
        res.setMjd(Double.toString(Double.parseDouble(res.getJd()) - 2400000.5));
        return res;
    }
}
普通の POJO
package com.mycompany.samplecxf;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "time")
public class JsonBean {
    private String cycric;
    private String gmt;
    private String jd;
    private String jp;
    private String jst;
    private String mjd;
    public JsonBean() {
        super();
    }
    /**
     * @return cycric
     */
    @XmlElement
    public final String getCycric() {
        return cycric;
    }
    /**
     * @return gmt
     */
    @XmlElement
    public final String getGmt() {
        return gmt;
    }
    /**
     * @return julian
     */
    @XmlElement
    public final String getJd() {
        return jd;
    }
    /**
     * @return jp
     */
    @XmlElement
    public final String getJp() {
        return jp;
    }
    /**
     * @return jst
     */
    @XmlElement
    public final String getJst() {
        return jst;
    }
    @XmlElement
    public final String getMjd() {
        return mjd;
    }
    /**
     * @param cycric セットする cycric
     */
    public final void setCycric(String cycric) {
        this.cycric = cycric;
    }
    /**
     * @param gmt セットする gmt
     */
    public final void setGmt(String gmt) {
        this.gmt = gmt;
    }
    /**
     * @param julian セットする julian
     */
    public final void setJd(String jd) {
        this.jd = jd;
    }
    /**
     * @param jp セットする jp
     */
    public final void setJp(String jp) {
        this.jp = jp;
    }
    /**
     * @param jst セットする jst
     */
    public final void setJst(String jst) {
        this.jst = jst;
    }
    /**
     * @param mjd セットする mjd
     */
    public final void setMjd(String mjd) {
        this.mjd = mjd;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("cycric,");
        sb.append("gmt,");
        sb.append("jd,");
        sb.append("jp,");
        sb.append("jst,");
        sb.append("mjd,");
        sb.append("\r\n");
        sb.append(getCycric()).append(",");
        sb.append(getGmt()).append(",");
        sb.append(getJd()).append(",");
        sb.append(getJp()).append(",");
        sb.append(getJst()).append(",");
        sb.append(getMjd());
        return sb.toString();
    }
}
 @GET
 @Produces("application/json")
 public Response conv(@QueryParam("time") String time) {
     return jsonJst(time);
 }
 
 @GET
 @Path("/json/jst/{jst}")
 @Produces("application/json")
 public Response jsonJst(@PathParam("jst") String jst) {
     try {
         return Response.ok(timeconv(jst, +9)).build();
     } catch (Exception e) {
         e.printStackTrace();
         return Response.serverError().build();
     }
 }
<servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping><jaxrs:server id="services" address="/">
return Response.ok(bean).build();で、bean の内容を適宜 JSON に返還してくれる。
return Response.serverError().build();
[~]$ curl http://localhost:8080/SampleCxf/rest/timeconv?time=20130405060708
{"jd":"2456387","cycric":"癸巳","gmt":"2013-04-04 21:07:08","jp":"平成25年04月05日",
"jst":"2013-04-05 06:07:08","mjd":"56386.5"}
[~]$ curl http://localhost:8080/SampleCxf/rest/timeconv/json/jst/20130405060708
{"jd":"2456387","cycric":"癸巳","gmt":"2013-04-04 21:07:08","jp":"平成25年04月05日",
"jst":"2013-04-05 06:07:08","mjd":"56386.5"}@POST
@Consume("application/json")
で、インタフェースメソッドの引数を JSON に対応する Bean にする。@GET
@Path("/xml/jst/{jst}")
@Produces("application/xml")
public Response xmlJst(@PathParam("jst") String jst) {
   try {
       return Response.ok(timeconv(jst, +9)).build();
   } catch (Exception e) {
       e.printStackTrace();
       return Response.serverError().build();
   }
}@XmlRootElement(name = "time")
public class JsonBean {
...
@XmlElement
public final String getCycric() {
   return cycric;
}
@XmlElement
public final String getGmt() {
   return gmt;
}
...
class と getter に @XMLElement をつける。タグ名を代えたければ (name= "tab") をつける[~]$ curl http://localhost:8080/SampleCxf/rest/timeconv/xml/jst/20130405060708 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <time> <cycric>癸巳</cycric> <gmt>2013-04-04 21:07:08</gmt> <jd>2456387</jd> <jp>平成25年04月05日</jp> <jst>2013-04-05 06:07:08</jst> <mjd>56386.5</mjd> </time>
@GET
@Path("/csv/jst/{jst}")
@Produces("text/plain")
public String csvJst(@PathParam("jst") String jst) {
    try {
        return timeconv(jst, +9).toString();
    } catch (Exception e) {
        e.printStackTrace();
        return "failed";
    }
}[~]$ curl http://localhost:8080/SampleCxf/rest/timeconv/csv/jst/20130405060708 cycric,gmt,jd,jp,jst,mjd, 癸巳,2013-04-04 21:07:08,2456387,平成25年04月05日,2013-04-05 06:07:08,56386.5
[~]$ curl http://localhost:8080/SampleCxf/rest/timeconv/json/jst/20130405060708?callback=ID1234
ID1234({"jd":"2456387","cycric":"癸巳","gmt":"2013-04-04 21:07:08","jp":"平成25年04月05日",
"jst":"2013-04-05 06:07:08","mjd":"56386.5"})package com.mycompany.samplecxf;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
@javax.ws.rs.ext.Provider
@javax.ws.rs.Consumes(value = {"application/json", "text/json"})
@javax.ws.rs.Produces(value = {"application/json", "text/json"})
public class MyJsonpProvider extends JacksonJsonProvider {
    @Context
    private HttpServletRequest request;
    
    public MyJsonpProvider() {
        super();
    }
    @Override
    public void writeTo(Object obj, Class<?> cls, Type genericType,
            Annotation[] anns, MediaType m,
            MultivaluedMap<String, Object> headers, OutputStream os)
            throws IOException {
        String fname = request.getParameter("callback");
        
        if (fname == null || "".equals(fname)) {
            // It's very important. The JSONPFunctionName is cached by JacksonJsonProvider.
            // So, we must null clear, if callback parameter on http-get is none.
            super.setJSONPFunctionName(null);
        } else {
            super.setJSONPFunctionName(fname);
        }
        super.writeTo(obj, cls, genericType, anns, m, headers, os);
    }
}
JacksonJsonProvider? を継承して、Bean を 文字列表記にする (Serialize) ときに、ID() を付け足すようにしたもの
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <context:property-placeholder/>
    <context:annotation-config/>
    <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer"/>
    <bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"/>
    <jaxrs:server id="services" address="/">
        <jaxrs:serviceBeans>
            <bean class="com.mycompany.samplecxf.HelloWorld" />
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <!-- <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/> -->
            <bean class="com.mycompany.samplecxf.MyJsonpProvider" />
        </jaxrs:providers>
    </jaxrs:server>
</beans>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset=utf-8>
        <title>JAX RS Ajax Example</title>
        <style type="text/css">
            /* ==================== STYLES ==================== */
            /* ==================== STYLES ==================== */
        </style>
        <script type="text/javascript"
        src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script type="text/javascript">
            /* ==================== SCRIPTS ==================== */
            var URL = 'rest/timeconv?callback=?';
            $(document).ready(function() {
                $('button').on('click', function() {
                    var arg = {
                        time : $('#date').val(),
                        type : "jst"
                    }
                    $.getJSON(URL, arg, function(data) {
                        console.log(data);
                        $('div#JST').text(data.jst);
                        $('div#CYCRIC').text(data.cycric);
                        $('div#JP').text(data.jp);
                        $('div#GMT').text(data.gmt);
                        $('div#JD').text(data.jd);
                        $('div#MJD').text(data.mjd);
                    });
                });
            });
            /* ==================== SCRIPTS ==================== */
        </script>
    </head>
    <body>
        <input type="text" id="date" value="20130405060708"/>
        <button>Translate Time</button>
        <table>
            <tbody>
                <tr><td>JST</td>  <td>:</td> <td><div id="JST"></div></td></tr>
                <tr><td>干支</td> <td>:</td> <td><div id="CYCRIC"></div></td></tr>
                <tr><td>和暦</td> <td>:</td> <td><div id="JP"></div></td></tr>
                <tr><td>GMT</td>  <td>:</td> <td><div id="GMT"></div></td></tr>
                <tr><td>ユリウス日</td> <td>:</td> <td><div id="JD"></div></td></tr>
                <tr><td>修正ユリウス日</td> <td>:</td> <td><div id="MJD"></div></td></tr>
            </tbody>
        </table>
    </body>
</html>
JSON や XML に変換する Bean に List<Sub> のフィールドを設定すれば良いだけ
XML に変換する場合には、デフォルトコンストラクタ (引数なしコンストラクタ public Entry(){}) を忘れずに
@XmlRootElement(name = "time")
public class JsonBean {
    private String cycric;
    private String gmt;
    private String jd;
    private String jp;
    private String jst;
    private String mjd;
    private List<Entry> person;
    @XmlElement
    public List<Entry> getPerson() {
        return person;
    }
    public void setPerson(List<Entry> person) {
        this.person = person;
    }
    
    (略)
@XmlRootElement
public class Entry {
    private String name;
    private String sex;
    
    public Entry(){
        super();
    }
    public Entry(String name, String sex) {
        this();
        this.name = name;
        this.sex = sex;
    }
    @XmlElement
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @XmlElement
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    
}
実行結果
[~]$ curl http://localhost:8080/SampleCxf/rest/timeconv/xml/jst/20130405060708 | tidy -xml -utf8 -indent
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   316  100   316    0     0  31246      0 --:--:-- --:--:-- --:--:-- 39500
No warnings or errors were found.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<time>
  <cycric>癸巳</cycric>
  <gmt>2013-04-04 21:07:08</gmt>
  <jd>2456387</jd>
  <jp>平成25年04月05日</jp>
  <jst>2013-04-05 06:07:08</jst>
  <mjd>56386.5</mjd>
  <person>
    <name>Sachi</name>
    <sex>female</sex>
  </person>
  <person>
    <name>Taro</name>
    <sex>male</sex>
  </person>
</time>
To learn more about HTML Tidy see http://tidy.sourceforge.net
Please send bug reports to html-tidy@w3.org
HTML and CSS specifications are available from http://www.w3.org/
Lobby your company to join W3C, see http://www.w3.org/Consortium
[~]$ curl http://localhost:8080/SampleCxf/rest/timeconv/json/jst/20130405060708 | python -mjson.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   207    0   207    0     0  17252      0 --:--:-- --:--:-- --:--:-- 20700
{
    "cycric": "\u7678\u5df3", 
    "gmt": "2013-04-04 21:07:08", 
    "jd": "2456387", 
    "jp": "\u5e73\u621025\u5e7404\u670805\u65e5", 
    "jst": "2013-04-05 06:07:08", 
    "mjd": "56386.5", 
    "person": [
        {
            "name": "Sachi", 
            "sex": "female"
        }, 
        {
            "name": "Taro", 
            "sex": "male"
        }
    ]
}