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以上に設定する。

<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 = 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  //いらないものが過分に含まれています。

<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は賢い子

2011年12月8日木曜日

[ASP]maxrequestlengthのエラー処理

ASPの場合、ファイルのアップロードの制限をしたいという時は、web.configでmaxrequestlengthを設定しますが、そのままだとmaxrequestlengthを超えた場合に”ページを表示できません”画面が表示されてしまいます。そのままだと、どういったエラーが出ているのかユーザに通知できないのでサポートも面倒くさいです。そこで、カスタムエラーを設定すればいいのかと思っていたら、ちょっと違うようです。

詳しくは、
http://www.developer.com/db/article.php/10920_3426051_2
にありますが、どうも日本語では見つからなかったので覚書です。


Sub Application_Error(ByVal sender As Object, _
 ByVal e As EventArgs)
    ' Fires when an error occurs
    ' Check to see whether we came 
    ' from the upload form
    If Path.GetFileName(Request.Path) = _
     "UploadForm.aspx" Then
        ' Get the error details
        Dim appException As System.Exception = _
         Server.GetLastError()
        Dim checkException As HttpException = _
         CType(appException, HttpException)
        ' Verify the expected error
        If checkException.GetHttpCode = 400 And _
         checkException.ErrorCode = -2147467259 Then
            ' Error 400 = bad request, user 
            ' tried to upload a file that's too large
            Session("ImageTooLarge") = True
            Server.ClearError()
            ' Go to the original target page
            Response.Redirect("UploadForm.aspx")
        End If
    End If
    ' For other errors, just accept the default processing
End Sub

MavenのJDKバージョン設定

激しく構成が乱れてるけど、とりあえず公開しておきます。
そのうち直します。


Mavenでコンパイルエラーが発生しました。


注釈は -source 1.3 でサポートされていません
(注釈を使用可能にするには、-source 5 以降を使用してください)
    @Override

MavenのデフォルトでJDK1.3を使用しているために発生しているようです。
JDK1.3だと注釈、型指定のリスト、Foreach等が使えず割と不便。
 JDKを変更するためにはpom.xmlのpluginに次の項を追加します。


 <build>
 <properties>
  <java.version>1.6</java.version>
 </properties>
 
 <plugins>
  <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>
 
参考
https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html

サーバのブルートフォース攻撃対策

気づいたら結構な頻度でブルートフォース攻撃されているようなので
次のようなiptablesの設定を追加しました。

$ sudo /sbin/iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --set
$ sudo /sbin/iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent  --update --seconds 60 --hitcount 4 -j DROP

 22ポートにコネクションが60秒に4回アクセスされると60秒アクセスを停止する。
 次のような結果が出たら成功。 

$ sudo /sbin/iptables --listtarget     prot opt source               destination         
DROP       tcp  --  anywhere             anywhere            tcp dpt:ssh state NEW recent: UPDATE seconds: 60 hit_count: 4 name: DEFAULT side: source 
           tcp  --  anywhere             anywhere            tcp dpt:ssh state NEW recent: SET name: DEFAULT side: source 
認証は128bitキーのみにしてるから気休めですが。
このあたりが詳しそう
 iptables の ipt_recent で ssh の brute force attack 対策

2011年10月27日木曜日

maven: jetty plugin ssl オレオレ証明書 の設定

jetty plugin 8.x での設定

1.keystore の作成
JDKのkeytoolでkeystoreを作成する。

keytool -keystore keystore -alias jetty -genkey -keyalg RSA -storepass (pass) -keypass (pass) -dname "CN=(domain)"


作成されたkeystoreを任意の場所へ


2.pom.xmlの編集
下記のようにpom.xmlにJetty pluginの設定を編集。
jetty-maven-pluginの"<connectors>"にSslSocketConnectorを追加する。

<connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector">
            <port>8443</port>
            <maxIdleTime>60000</maxIdleTime>
            <keystore>(任意のkeystoreへのパス)</keystore>
            <password>(pass)</password>
            <keyPassword>(pass)</keyPassword>
      </connector>


3.パスワードの暗号化
pom.xmlにパスワードを平文で保存するのは良くないので、
暗号化する。
java -cp jetty-http-8.x.jar:jetty-util-8.x.jar org.eclipse.jetty.http.security.Password (pass)


を実行すると、(pass)->jetty6の場合
jetty6
OBF:1ktv1x0r1z0f1z0f1x1v1kqz
MD5:e032a01e5c4dbb03a29fb031f6b37658


というような値が取得できるので、OBFまたはMD5をpom.xmlに入力する。





ついでに、iPhone等の開発でオレオレ証明書を利用する場合は、
NSURLRequestなら次のようなimplementの追加

@implementation NSURLRequest(NSHTTPURLRequest)

+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host
{
   return YES; // Or whatever logic
}
@end


ASIHttpRequestならvalidatesSecureCertificate = NOでOK

2011年9月8日木曜日

Ubuntu vmdkのマウント

書きかけでずっと放置してた投稿なので情報が古いかも。


VMware player, serverの仮想ディスクのvmdkの中身を見るためだけに
いちいちVMware playerを実行するのも面倒です。

そんなときにはvmdkをマウントしてくれるvmware-mountが便利です。

VMware playerには付属していないので、
VMware serverをダウンロードしなおして利用します。

 VMware server の Linux版だと、tar.gzのbinフォルダ以下にあるvmware-mountを
任意の場所に保存します。


vmware-mountを使用する。

1.マウントするvmdk上のパーティションを検索する。
vmware-mount -p (vmdkファイル)

例) 次のように表示される。
 Nr      Start       Size Type Id Sytem                 
-- ---------- ---------- ---- -- ------------------------
 1         63  268430022 BIOS  7 HPFS/NTFS


2.パーティションを指定してマウントする。
sudo vmware-mount (vmdkファイル) 1 (マウント先フォルダ)

 1.の例の場合、パーティション 1を指定する。

  例) sudo vmware-mount  vmimage.vmdk 1  /mnt/vmdk


3.パーティションをアンマウントする。
sudo vmware-mount -x