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