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" } ] }