2014年2月22日土曜日
Jersey2.6(JAX-RS2.2) with Jetty9 セットアップ
JAX-RS2.*を使いたいと思って、Jersey2.6をJetty9に組み込もうとしたのですが、
四苦八苦したのでメモ
いろいろdependancyに入れすぎてたのが問題だったようです。
jsr311-apiをdependancyから取り除くとうまくいきました。
JAX-RS2.*では、Pojomappingを指定するのではなく
JAX-RS JSON Providerに対応するライブラリを追加することでJSONに対応します。
下の例では、jackson-jaxrs-json-providerを追加しています。
JavaEE3以上は、jersey-container-servlet-coreを使うよう言われますが
自分は以前のweb.xmlでの指定方法が好みなので
jersey-container-servletを使っています。
pom.xml :
<略>
<properties>
<java.version>1.7</java.version>
<jettyVersion>9.1.2.v20140210</jettyVersion>
<jerseyVersion>2.6</jerseyVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jettyVersion}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jettyVersion}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jerseyVersion}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jerseyVersion}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jettyVersion}</version>
<configuration>
<webAppSourceDirectory>${basedir}/webapp</webAppSourceDirectory>
<webXmlFile>${basedir}/webapp/WEB-INF/web.xml</webXmlFile>
<jettyEnvXml>${basedir}/webapp/WEB-INF/jetty-env.xml</jettyEnvXml>
<contextPath>/</contextPath>
</configuration>
</plugin>
</plugins>
</build>
WEB-INF/web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>パッケージ</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>
WEB-INF/jetty-env.xml:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
<Call name="setAttribute">
<Arg>org.eclipse.jetty.webapp.configuration</Arg>
<Arg>
<Array type="java.lang.String">
<Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
<Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
<Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
<Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
<Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
<Item>org.eclipse.jetty.webapp.TagLibConfiguration</Item>
</Array>
</Arg>
</Call>
<Set name="war"><SystemProperty name="jetty.home" default="."/>/</Set>
</Configure>
2013年11月15日金曜日
Jersey ApacheHttpClientをつかう
Javaでhttpから何かを取得する場合に、
一番簡単に扱えてかつ拡張性が高いのは
Jersey ApacheHttpClient だとおもいますという話。
ApacheHttpClientの特徴は、オブジェクトマッピングをしてくれるという点です
String型で取得する場合は次のようにすればよい
String res = ApacheHttpClient.create().resource(urlString).get(String.class);
一行ですんじゃいます。
またJSONやXMLを取得する場合にJAXBによる変換を行うことができます。
取得するXMLがつぎのような場合、
<?xml version="1.0"?>
<date>2012/10/11</date>
<hash>hogehogegeee</hash>
<rank>1</rank>
<value>2</value>
<addTime>2012/10/11 12:00</addTime>
マッピングをするクラスを次のように定義して、
Ranking.java :
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Ranking {
@XmlElement
public Date date;
@XmlElement
public String hash;
@XmlElement
public int rank;
@XmlElement
public long value;
@XmlElement
public Date addTime;
public Ranking(){}
}
で、次のようにすれば指定したクラスにマッピングして取得することができます。
Ranking ranking = restClient.resource(urlString).get(Ranking.class);
タイムアウトの設定
接続タイムアウト
ApacheHttpClient client = ApacheHttpClient.create();
client.setConnectTimeout(360000);
読み込みタイムアウト
client.setReadTimeout(900000);
フィルターを定義することによって、通信の前後の挙動を
ファンクションクラス?として定義して使いまわすことができます。
クッキーを管理してセッション等を保持する場合は下のような
フィルターを登録すればよいようです。
ApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
config.getProperties().put(ApacheHttpClientConfig.PROPERTY_HANDLE_COOKIES, true);
ApacheHttpClient client = ApacheHttpClient.create(config);
client.addFilter(new ClientFilter() {
private ArrayList
単にCookieを渡したいだけならWebResourceから指定もできます。
ApacheHttpClient client = ApacheHttpClient.create();
String res = client
.resource(url)
.header(HttpHeaders.COOKIE, cookieString)
.get(String.class);
Jersey Clientと同じようにFilterで登録するかWebResourceにヘッダーを追加する方法です。
Filterを追加する場合:
ApacheHttpClient client = ApacheHttpClient.create();
client.addFilter(new ClientFilter() {
@Override
public ClientResponse handle(ClientRequest request)
throws ClientHandlerException {
request.getHeaders().put("UserAgent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9b5) Gecko/2008032619 Firefox/3.0b5");
return getNext().handle(request);
}
});
WebResourceに追加する場合:
ApacheHttpClient client = ApacheHttpClient.create();
String res = client
.resource(url)
.header(HttpHeaders.USER_AGENT, userAgent)
.get(String.class);
Client作成時に渡します。
ApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
config.getProperties().put(
ApacheHttpClientConfig.PROPERTY_PROXY_URI,
"proxyurl");
ApacheHttpClient client = ApacheHttpClient.create(config);
Java自体の機能を利用します。
System.setProperty("socksProxyHost", "127.0.0.1");
System.setProperty("socksProxyPort", "9050");
SocksプロクシとApacheHttpClientのconfigでのプロクシの設定は
同時に利用可能です。両方設定した場合、Socksが優先されます。
一番簡単に扱えてかつ拡張性が高いのは
Jersey ApacheHttpClient だとおもいますという話。
通常の使い方
ApacheHttpClientの特徴は、オブジェクトマッピングをしてくれるという点です
String型で取得する場合は次のようにすればよい
String res = ApacheHttpClient.create().resource(urlString).get(String.class);
一行ですんじゃいます。
またJSONやXMLを取得する場合にJAXBによる変換を行うことができます。
取得するXMLがつぎのような場合、
<?xml version="1.0"?>
<date>2012/10/11</date>
<hash>hogehogegeee</hash>
<rank>1</rank>
<value>2</value>
<addTime>2012/10/11 12:00</addTime>
マッピングをするクラスを次のように定義して、
Ranking.java :
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Ranking {
@XmlElement
public Date date;
@XmlElement
public String hash;
@XmlElement
public int rank;
@XmlElement
public long value;
@XmlElement
public Date addTime;
public Ranking(){}
}
で、次のようにすれば指定したクラスにマッピングして取得することができます。
Ranking ranking = restClient.resource(urlString).get(Ranking.class);
よくある設定
タイムアウトの設定
接続タイムアウト
ApacheHttpClient client = ApacheHttpClient.create();
client.setConnectTimeout(360000);
読み込みタイムアウト
client.setReadTimeout(900000);
フィルターの定義
フィルターを定義することによって、通信の前後の挙動を
ファンクションクラス?として定義して使いまわすことができます。
Cookieの管理
クッキーは検証してないのですが、コンフィグで渡せば使えそうです。クッキーを管理してセッション等を保持する場合は下のような
フィルターを登録すればよいようです。
ApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
config.getProperties().put(ApacheHttpClientConfig.PROPERTY_HANDLE_COOKIES, true);
ApacheHttpClient client = ApacheHttpClient.create(config);
client.addFilter(new ClientFilter() {
private ArrayList
単にCookieを渡したいだけならWebResourceから指定もできます。
ApacheHttpClient client = ApacheHttpClient.create();
String res = client
.resource(url)
.header(HttpHeaders.COOKIE, cookieString)
.get(String.class);
UserAgentの設定
UserAgentの設定は2つの方法があります。Jersey Clientと同じようにFilterで登録するかWebResourceにヘッダーを追加する方法です。
Filterを追加する場合:
ApacheHttpClient client = ApacheHttpClient.create();
client.addFilter(new ClientFilter() {
@Override
public ClientResponse handle(ClientRequest request)
throws ClientHandlerException {
request.getHeaders().put("UserAgent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9b5) Gecko/2008032619 Firefox/3.0b5");
return getNext().handle(request);
}
});
WebResourceに追加する場合:
ApacheHttpClient client = ApacheHttpClient.create();
String res = client
.resource(url)
.header(HttpHeaders.USER_AGENT, userAgent)
.get(String.class);
プロキシの設定
HTTP プロクシの設定にはApacheHttpClientConfigをClient作成時に渡します。
ApacheHttpClientConfig config = new DefaultApacheHttpClientConfig();
config.getProperties().put(
ApacheHttpClientConfig.PROPERTY_PROXY_URI,
"proxyurl");
ApacheHttpClient client = ApacheHttpClient.create(config);
SocksプロクシとHTTPプロクシの優先順位
SocksプロクシにはApacheHttpClient自体は対応していないので、Java自体の機能を利用します。
System.setProperty("socksProxyHost", "127.0.0.1");
System.setProperty("socksProxyPort", "9050");
SocksプロクシとApacheHttpClientのconfigでのプロクシの設定は
同時に利用可能です。両方設定した場合、Socksが優先されます。
2013年10月5日土曜日
JAXB アノテーションを利用したJSON マッピング
Jerseyを使っているとレスポンス用のJSONの定義クラスに
アノテーションの@XmlTransitで省略したり
@XmlElement(name="element")で名前変更したりして
ObjectをJAXB経由でJSONに自動でパースさせてたりしますが
その定義を他でも使いまわして各々の処理でJSON文字列にパースする場合には
JacksonJAXBAnnotations
を利用すればいいです。
これは利用すればJAXBアノテーションを利用したマッピングが可能です。
mavenでpom.xmlに追加
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.13</version>
</dependency>
Javaでは次のようにするればobjをJSONにパース可能
例ではやってないけどObjectMapperはthread safeなので使いまわしましょう
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
…
JaxbAnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
ObjectWriter ow = new ObjectMapper().setAnnotationIntrospector(introspector).writer()
.withDefaultPrettyPrinter();
String json = ow.writeValueAsString(obj);
最初は、jerseyの内部ロジックを利用しようとしましたが
ひとつのオブジェクトしかないリストがオブジェクトでパースされる問題が発生したり
いちいちクラスタイプ指定しないといけないので面倒でやめました
ちなみに 最初はこんなかんじにしてました
private Class[] types = {
MyClass.class,
ArrayList.class,
};
String result = null;
JSONConfiguration config = JSONConfiguration.natural().build();
JSONJAXBContext ctx = new JSONJAXBContext(config, types);
JSONMarshaller m = ctx.createJSONMarshaller();
StringWriter sw = new StringWriter();
m.marshallToJSON(xmlObj, sw);
result = sw.toString();
return result;
JAXBContextResolverと似たような感じですね
POJOMappingFeatureで問題はどうにかなるかと思ったんですが
JacksonJAXBAnnotationsを使ったほうが楽だし早い
アノテーションの@XmlTransitで省略したり
@XmlElement(name="element")で名前変更したりして
ObjectをJAXB経由でJSONに自動でパースさせてたりしますが
その定義を他でも使いまわして各々の処理でJSON文字列にパースする場合には
JacksonJAXBAnnotations
を利用すればいいです。
これは利用すればJAXBアノテーションを利用したマッピングが可能です。
mavenでpom.xmlに追加
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.13</version>
</dependency>
Javaでは次のようにするればobjをJSONにパース可能
例ではやってないけどObjectMapperはthread safeなので使いまわしましょう
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
…
JaxbAnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
ObjectWriter ow = new ObjectMapper().setAnnotationIntrospector(introspector).writer()
.withDefaultPrettyPrinter();
String json = ow.writeValueAsString(obj);
最初は、jerseyの内部ロジックを利用しようとしましたが
ひとつのオブジェクトしかないリストがオブジェクトでパースされる問題が発生したり
いちいちクラスタイプ指定しないといけないので面倒でやめました
ちなみに 最初はこんなかんじにしてました
private Class[] types = {
MyClass.class,
ArrayList.class,
};
String result = null;
JSONConfiguration config = JSONConfiguration.natural().build();
JSONJAXBContext ctx = new JSONJAXBContext(config, types);
JSONMarshaller m = ctx.createJSONMarshaller();
StringWriter sw = new StringWriter();
m.marshallToJSON(xmlObj, sw);
result = sw.toString();
return result;
JAXBContextResolverと似たような感じですね
POJOMappingFeatureで問題はどうにかなるかと思ったんですが
JacksonJAXBAnnotationsを使ったほうが楽だし早い
2013年9月16日月曜日
Jetty SSL設定
Jetty 8.*での情報です
9.*でも利用可能かもしれません
#証明書作成用キーの作成
openssl genrsa -des3 -out jetty.key
openssl req -new -x509 -key jetty.key -out jetty.crt
#サーバへ送る証明書の作成
openssl req -new -key jetty.key -out jetty.csr
#2048bit以上が必要な場合
#openssl req -nodes -newkey rsa:2048 -keyout jetty.key -out jetty.csr
#keytoolがない場合${JAVA_HOME}/jre/bin/keytool
#証明書の登録
#jetty.csrの内容を証明サーバに送る
#jetty.crtに帰ってきた証明書を保存する
#keystoreへ登録
keytool -keystore keystore -import -alias jetty -file jetty.crt -trustcacerts
#pkcs12形式を登録
openssl pkcs12 -export -inkey jetty.key -in jetty.crt -certfile etc/SSL_CA.cer(中間証明書) -out jetty.pkcs12
keytool -importkeystore -srckeystore jetty.pkcs12 -srcstoretype PKCS12 -destkeystore etc/keystore
#jetty-ssl.xml修正
<New id="sslContextFactory" class="org.eclipse.jetty.http.ssl.SslContextFactory">
<Set name="KeyStore"><Property name="jetty.home" default="." />/etc/keystore</Set>
<Set name="KeyStorePassword">[keystore password]</Set>
<Set name="KeyManagerPassword">[key password]</Set>
<Set name="TrustStore"><Property name="jetty.home" default="." />/etc/keystore</Set>
<Set name="TrustStorePassword">[keystore password]</Set>
</New>
#verify
openssl s_client -connect {対象サーバ}:443 -showcerts
参考
http://wiki.eclipse.org/Jetty/Howto/Configure_SSL
9.*でも利用可能かもしれません
#証明書作成用キーの作成
openssl genrsa -des3 -out jetty.key
openssl req -new -x509 -key jetty.key -out jetty.crt
#サーバへ送る証明書の作成
openssl req -new -key jetty.key -out jetty.csr
#2048bit以上が必要な場合
#openssl req -nodes -newkey rsa:2048 -keyout jetty.key -out jetty.csr
#keytoolがない場合${JAVA_HOME}/jre/bin/keytool
#証明書の登録
#jetty.csrの内容を証明サーバに送る
#jetty.crtに帰ってきた証明書を保存する
#keystoreへ登録
keytool -keystore keystore -import -alias jetty -file jetty.crt -trustcacerts
#pkcs12形式を登録
openssl pkcs12 -export -inkey jetty.key -in jetty.crt -certfile etc/SSL_CA.cer(中間証明書) -out jetty.pkcs12
keytool -importkeystore -srckeystore jetty.pkcs12 -srcstoretype PKCS12 -destkeystore etc/keystore
#jetty-ssl.xml修正
<New id="sslContextFactory" class="org.eclipse.jetty.http.ssl.SslContextFactory">
<Set name="KeyStore"><Property name="jetty.home" default="." />/etc/keystore</Set>
<Set name="KeyStorePassword">[keystore password]</Set>
<Set name="KeyManagerPassword">[key password]</Set>
<Set name="TrustStore"><Property name="jetty.home" default="." />/etc/keystore</Set>
<Set name="TrustStorePassword">[keystore password]</Set>
</New>
#verify
openssl s_client -connect {対象サーバ}:443 -showcerts
参考
http://wiki.eclipse.org/Jetty/Howto/Configure_SSL
2013年1月5日土曜日
HttpServletRequestのthread内共有
ASP.NETを利用していると
static HttpContext HttpContext.Current からリクエスト情報を取得して
IPADDRESS等の情報をどこででも取得することができます
似たような意識でJavaで実装していると
どうしてもHttpServletRequestの取得でつまずいてしまいます。
そこで、スレッド毎に値を保持するThreadLocalを利用して
HttpServletRequest をスレッド内で参照できるようにします
private final static ThreadLocal<HttpServletRequest> servletRequests = new ThreadLocal<HttpServletRequest>();
public static void setServletRequest(HttpServletRequest request) {
servletRequests.set(request);
}
public static HttpServletRequest getServletRequest() {
return servletRequests.get();
}
もちろん非同期な実装では参照できなくなる(かな?)
リクエスト初期化時にセットする必要があります。
リクエスト終了後はガベージコレクション対象になります。
static HttpContext HttpContext.Current からリクエスト情報を取得して
IPADDRESS等の情報をどこででも取得することができます
似たような意識でJavaで実装していると
どうしてもHttpServletRequestの取得でつまずいてしまいます。
そこで、スレッド毎に値を保持するThreadLocalを利用して
HttpServletRequest をスレッド内で参照できるようにします
private final static ThreadLocal<HttpServletRequest> servletRequests = new ThreadLocal<HttpServletRequest>();
public static void setServletRequest(HttpServletRequest request) {
servletRequests.set(request);
}
public static HttpServletRequest getServletRequest() {
return servletRequests.get();
}
もちろん非同期な実装では参照できなくなる(かな?)
リクエスト初期化時にセットする必要があります。
リクエスト終了後はガベージコレクション対象になります。
2012年12月28日金曜日
Jersey (JAX-RS) での JSON 取り扱いTIPS
JerseyはレスポンスとしてXMLを前提にしていて
JSON扱おうとするとたまに問題が起こるのでその対応をメモ
1.Listに要素がひとつしかないとArrayではなくObject渡してくる
このあたり参考にして、サーブレットを登録しているパッケージ中に
@Providerを定義する
http://tugdualgrall.blogspot.jp/2011/09/jax-rs-jersey-and-single-element-arrays.html
2.…to be continued
もしくは、stackoverflowにJSONパーサをJacksonに変更する方法が乗っていたので
これを利用すると全て解決しそう(うまく動かなかった)
how-can-i-customize-serialization-of-a-list-of-jaxb-objects-to-json
追記:
JSONの扱いは上記の方法を取る必要はなく、POJOMappingFeatureを利用すればいいもよう
Web.xmlのサーブレット定義に追加します
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
JSON扱おうとするとたまに問題が起こるのでその対応をメモ
1.Listに要素がひとつしかないとArrayではなくObject渡してくる
このあたり参考にして、サーブレットを登録しているパッケージ中に
@Providerを定義する
http://tugdualgrall.blogspot.jp/2011/09/jax-rs-jersey-and-single-element-arrays.html
2.…to be continued
もしくは、stackoverflowにJSONパーサをJacksonに変更する方法が乗っていたので
これを利用すると全て解決しそう(うまく動かなかった)
how-can-i-customize-serialization-of-a-list-of-jaxb-objects-to-json
追記:
JSONの扱いは上記の方法を取る必要はなく、POJOMappingFeatureを利用すればいいもよう
Web.xmlのサーブレット定義に追加します
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
2012年12月6日木曜日
Jersey の HttpServletRequest.getParameter仕様
Jerseyの開発でつまづいたので共有
JerseyでPost時のパラメータを取得するには、普段は@FormParamを利用します。
が、既存のライブラリを利用する際に、HttpServletRequest.getParameter, getParameterMap
を通して取得したい場合があります。
ただ、Jerseyの仕様でgetParameterはnullが返ってきます。
(内部のinputstreamがなくなっているからとか)
その場合には手段がいくつかあります。
1.filterを利用する
2.ServletContainerをextendsする
上記を利用して@FormParam生成前に内部的にparameterを保持しておく
という案がありますが、資料なさ過ぎて私にはむりです。
そこでとりあえず
3.@Consumesを使ってFormデータを直接受け取る
を利用して既存ライブラリのほう
Postは
JerseyでPost時のパラメータを取得するには、普段は@FormParamを利用します。
が、既存のライブラリを利用する際に、HttpServletRequest.getParameter, getParameterMap
を通して取得したい場合があります。
ただ、Jerseyの仕様でgetParameterはnullが返ってきます。
(内部のinputstreamがなくなっているからとか)
その場合には手段がいくつかあります。
1.filterを利用する
2.ServletContainerをextendsする
追記(2014/04/15): jetty 9あたリから@Injectを利用した方法があるようです要調査上記を利用して@FormParam生成前に内部的にparameterを保持しておく
という案がありますが、資料なさ過ぎて私にはむりです。
そこでとりあえず
3.@Consumesを使ってFormデータを直接受け取る
を利用して既存ライブラリのほう
を修正しました。Postは
"application/x-www-form-urlencoded"のcontent-typeでデータを渡してくるので @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void post(MultivaluedMap formParams) { …
というふうにデータ取得ができます。でこれをHttpServletRequestのgetPrameterに詰め込めればいいのですがあきらめ。既存ライブラリでgetParameterする際にデータを結合しました。2012年1月15日日曜日
jetty + jersey + Amazon Product Advertising APIのサンプル
●xsdからjarファイルの作成
AmazonのAmazon Product Advertising APIを利用する際には、
jaxbでxmlをパースするためにクラスの定義が必要です。
ここでは、アマゾンの提供しているxsdからクラス定義を含むjarファイルを作成します。
まず、アマゾンからAWSECommerceService.xsd をダウンロードし、
src/main/resourcesに設置。
pom.xmlを下記のように追加。
maven-compiler-pluginは、1.5以上に設定する。
maven packageを実行すると、target以下にjarファイルが作成されます。
(著者は、target/generated-sources/xjc以下のjavaファイルをインポートして
利用しているのでjarが使用可能か未確認です。)
●signerの実装
2009年8月15日以降、Product Advertising APIは、リクエストに署名認証を含めなければならなくなりました。
下記にAmazonの提供している署名のサンプルがあります。
Java Sample Code for Calculating Signature Version 2 Signatures
このサンプルのままでは、リクエストに改行が入ってしまって動かないので、
76行目の
Base64 encoder = new Base64();
を
Base64 encoder = new Base64(0);
に変更する必要があります。
それと、日本での検索をしたい場合は、
33行目 endpoint を "ecs.amazonaws.jp"に変更します。
●取得してみる
作成したjar、signerをインポートしてjerseyを利用して検索結果をそのまま表示する
サンプルを作ってみます。
src/main/java/gauuud/amazon/service/ItemSearchResource.java
package gauuud.amazon.service;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.amazon.associates.sample.SignedRequestsHelper;
import com.sun.jersey.api.client.Client;
import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearchResponse;
@Path("/item_search")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public class ItemSearchResource {
@GET
public ItemSearchResponse get(String word) throws Exception {
SignedRequestsHelper signer = new SignedRequestsHelper();
Map map = new HashMap();
map.put("Service","AWSECommerceService");
map.put("Operation","ItemSearch");
map.put("Version","2011-08-01");
map.put("SearchIndex","Books");
map.put("Keywords","java");
map.put("AssociateTag","[アソシエートID]");
String url = signer.sign(map);
Client c = Client.create();
ItemSearchResponse result = c.resource(url).get(ItemSearchResponse.class);
return result;
}
}
pom.xml //いらないものが過分に含まれています。
maven jettry:run
を実行して
localhost:8080/rest/search_item
にアクセスするとxmlが取得できるはず。
AmazonのAmazon Product Advertising APIを利用する際には、
jaxbでxmlをパースするためにクラスの定義が必要です。
ここでは、アマゾンの提供しているxsdからクラス定義を含むjarファイルを作成します。
まず、アマゾンからAWSECommerceService.xsd をダウンロードし、
src/main/resourcesに設置。
pom.xmlを下記のように追加。
maven-compiler-pluginは、1.5以上に設定する。
<groupId>test</groupId>
<artifactId>aws-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>AWSECommerceService</name>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb22-plugin</artifactId>
<version>0.8.0</version>
</plugin>
</plugins>
</build>
maven packageを実行すると、target以下にjarファイルが作成されます。
(著者は、target/generated-sources/xjc以下のjavaファイルをインポートして
利用しているのでjarが使用可能か未確認です。)
●signerの実装
2009年8月15日以降、Product Advertising APIは、リクエストに署名認証を含めなければならなくなりました。
下記にAmazonの提供している署名のサンプルがあります。
Java Sample Code for Calculating Signature Version 2 Signatures
このサンプルのままでは、リクエストに改行が入ってしまって動かないので、
76行目の
Base64 encoder = new Base64();
を
Base64 encoder = new Base64(0);
に変更する必要があります。
それと、日本での検索をしたい場合は、
33行目 endpoint を "ecs.amazonaws.jp"に変更します。
●取得してみる
作成したjar、signerをインポートしてjerseyを利用して検索結果をそのまま表示する
サンプルを作ってみます。
src/main/java/gauuud/amazon/service/ItemSearchResource.java
package gauuud.amazon.service;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.amazon.associates.sample.SignedRequestsHelper;
import com.sun.jersey.api.client.Client;
import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearchResponse;
@Path("/item_search")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public class ItemSearchResource {
@GET
public ItemSearchResponse get(String word) throws Exception {
SignedRequestsHelper signer = new SignedRequestsHelper();
Map
map.put("Service","AWSECommerceService");
map.put("Operation","ItemSearch");
map.put("Version","2011-08-01");
map.put("SearchIndex","Books");
map.put("Keywords","java");
map.put("AssociateTag","[アソシエートID]");
String url = signer.sign(map);
Client c = Client.create();
ItemSearchResponse result = c.resource(url).get(ItemSearchResponse.class);
return result;
}
}
pom.xml //いらないものが過分に含まれています。
<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>gauuud</groupId> <artifactId>aws-sample</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>aws-sample</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.codehaus.jettison</groupId> <artifactId>jettison</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> <version>1.11</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> <version>1.11</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-server</artifactId> <version>1.11</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId> <version>1.11</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-json</artifactId> <version>1.11</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>8.0.0.v20110901</version> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> <webAppSourceDirectory>${basedir}/webapp</webAppSourceDirectory> <webXmlFile>${basedir}/webapp/WEB-INF/web.xml</webXmlFile> <contextPath>/</contextPath> <connectors> <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector"> <port>8080</port> <maxIdleTime>60000</maxIdleTime> </connector> </connectors> <systemProperties> <systemProperty> <name>org.apache.commons.logging.Log</name> <value>org.apache.commons.logging.impl.SimpleLog</value> </systemProperty> </systemProperties> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>6</source> <target>6</target> </configuration> </plugin> </plugins> </build> </project>src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>aws-sample</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>gauuud.amazon.service</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>maven jettry:run
を実行して
localhost:8080/rest/search_item
にアクセスするとxmlが取得できるはず。
2011年12月15日木曜日
Jersey Client 利用時の文字化け on CentOS5
Sakura VPS 上でJersey Client API を使った、
JSONを取得して、加工、受け渡しするようなRESTful APIを作成していたときに
文字化けが発生しました。
すべてUTF-8で統一されているのになぜ?とあせりました。
Java内部の問題なのかとも思いましたが、開発環境のUbuntuでは
発生してなかったので、ない。
受信周りの問題だなといろいろ調べた結果、
結局はロケールの設定の問題でした。
開発環境のUbuntuでは、
$ locale
の結果が、
$ locale
LANG=ja_JP.utf8
LC_CTYPE="ja_JP.utf8"
LC_NUMERIC="ja_JP.utf8"
LC_TIME="ja_JP.utf8"
LC_COLLATE="ja_JP.utf8"
LC_MONETARY="ja_JP.utf8"
LC_MESSAGES="ja_JP.utf8"
LC_PAPER="ja_JP.utf8"
LC_NAME="ja_JP.utf8"
LC_ADDRESS="ja_JP.utf8"
LC_TELEPHONE="ja_JP.utf8"
LC_MEASUREMENT="ja_JP.utf8"
LC_IDENTIFICATION="ja_JP.utf8"
LC_ALL=
のようになっているのに対してCentOSは、
$ locale
LANG=C
LC_CTYPE="C"
…
C…、よくわからない値が入っていました。
なので、localeを編集 して、再起動しました。
$ vi /etc/sysconfig/i18n
LANG="ja_JP.UTF-8"
結果、JSON取得時の文字化けはなくなりました。
Jerseyは賢い子
JSONを取得して、加工、受け渡しするようなRESTful APIを作成していたときに
文字化けが発生しました。
すべてUTF-8で統一されているのになぜ?とあせりました。
Java内部の問題なのかとも思いましたが、開発環境のUbuntuでは
発生してなかったので、ない。
受信周りの問題だなといろいろ調べた結果、
結局はロケールの設定の問題でした。
開発環境のUbuntuでは、
$ locale
の結果が、
$ locale
LANG=ja_JP.utf8
LC_CTYPE="ja_JP.utf8"
LC_NUMERIC="ja_JP.utf8"
LC_TIME="ja_JP.utf8"
LC_COLLATE="ja_JP.utf8"
LC_MONETARY="ja_JP.utf8"
LC_MESSAGES="ja_JP.utf8"
LC_PAPER="ja_JP.utf8"
LC_NAME="ja_JP.utf8"
LC_ADDRESS="ja_JP.utf8"
LC_TELEPHONE="ja_JP.utf8"
LC_MEASUREMENT="ja_JP.utf8"
LC_IDENTIFICATION="ja_JP.utf8"
LC_ALL=
のようになっているのに対してCentOSは、
$ locale
LANG=C
LC_CTYPE="C"
…
C…、よくわからない値が入っていました。
なので、localeを編集 して、再起動しました。
$ vi /etc/sysconfig/i18n
LANG="ja_JP.UTF-8"
結果、JSON取得時の文字化けはなくなりました。
Jerseyは賢い子
登録:
投稿 (Atom)