The net is vast
プログラミングや、コンピュータなどの備忘録です。 主にRuby, Java, Linux, 等を扱います。アルゴリズムも扱いたいな。
ラベル aws の投稿を表示しています。 すべての投稿を表示
ラベル aws の投稿を表示しています。 すべての投稿を表示
16:26

Product Advertising APIの日本用Restクライアント(Java)

Category: , By jx
dankogaiさんが、PerlのProduct Advertising APIライブラリを作ってた。便乗して、Java。 Amazonの新しいProduct Advertising APIのRestのリクエストの作り方は前回のエントリで説明しました。このRestクライアントは、Amazonのドキュメントに載っています。しかし、その例は米国用になっているので、ちょっとだけ修正してあげる必要が在ります。 具体的には、endpointをecs.amazonaws.jpに変更します。これだけで、日本のものに適用出来るようになります。なお、使用方法ですが、次のようにします。簡単ですね。
  // リクエストのキーと値のMap
  Map<String, String> keyMap = new HashMap<String, String>();
  keyMap.put("AWSAccessKeyId", "000000000000000");
  keyMap.put("Version", "2008-08-19");
  keyMap.put("Operation", "ItemLookup");
  keyMap.put("ItemId", "4877712399");
  keyMap.put("ResponseGroup", "Medium");
  keyMap.put("AssociateTag", "jx-22");
  keyMap.put("Service", "AWSECommerceService");
  SignedRequestsHelper signedRequestsHelper = new SignedRequestsHelper();
  String urlStr = signedRequestsHelper.sign(keyMap);
  URL url = new URL(urlStr);
  HttpURLConnection connection = (HttpURLConnection) url
    .openConnection();
日本用に修正したJava Rest クライアント
package com.amazon.associates.sample;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import net.jirox.shohyo.exception.ShohyoFatalException;

import org.apache.commons.codec.binary.Base64;

public class SignedRequestsHelper {
 private static final String UTF8_CHARSET = "UTF-8";
 private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
 private static final String REQUEST_URI = "/onca/xml";
 private static final String REQUEST_METHOD = "GET";

 //日本用
 private String endpoint = "ecs.amazonaws.jp"; // must be lowercase
 private String awsAccessKeyId = "YOUR ACCESS KEY ID";
 private String awsSecretKey = "YOUR SECRET KEY";

 private SecretKeySpec secretKeySpec = null;
 private Mac mac = null;

 public SignedRequestsHelper() throws ShohyoFatalException {
  byte[] secretyKeyBytes;
  try {
   secretyKeyBytes = awsSecretKey.getBytes(UTF8_CHARSET);
   secretKeySpec = new SecretKeySpec(secretyKeyBytes,
     HMAC_SHA256_ALGORITHM);
   mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
   mac.init(secretKeySpec);
  } catch (UnsupportedEncodingException e) {
   throw new ShohyoFatalException(e);
  } catch (NoSuchAlgorithmException e) {
   throw new ShohyoFatalException(e);
  } catch (InvalidKeyException e) {
   throw new ShohyoFatalException(e);
  }
 }

 public String sign(Map<String, String> params) {
  params.put("AWSAccessKeyId", awsAccessKeyId);
  params.put("Timestamp", timestamp());

  SortedMap<String, String> sortedParamMap = new TreeMap<String, String>(
    params);
  String canonicalQS = canonicalize(sortedParamMap);
  String toSign = REQUEST_METHOD + "\n" + endpoint + "\n" + REQUEST_URI
    + "\n" + canonicalQS;

  String hmac = hmac(toSign);
  String sig = percentEncodeRfc3986(hmac);
  String url = "http://" + endpoint + REQUEST_URI + "?" + canonicalQS
    + "&Signature=" + sig;

  return url;
 }

 private String hmac(String stringToSign) {
  String signature = null;
  byte[] data;
  byte[] rawHmac;
  try {
   data = stringToSign.getBytes(UTF8_CHARSET);
   rawHmac = mac.doFinal(data);
   Base64 encoder = new Base64();
   signature = new String(encoder.encode(rawHmac));
  } catch (UnsupportedEncodingException e) {
   throw new RuntimeException(UTF8_CHARSET + " is unsupported!", e);
  }
  return signature;
 }

 private String timestamp() {
  String timestamp = null;
  Calendar cal = Calendar.getInstance();
  DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
  dfm.setTimeZone(TimeZone.getTimeZone("GMT"));
  timestamp = dfm.format(cal.getTime());
  return timestamp;
 }

 private String canonicalize(SortedMap<String, String> sortedParamMap) {
  if (sortedParamMap.isEmpty()) {
   return "";
  }

  StringBuffer buffer = new StringBuffer();
  Iterator<Map.Entry<String, String>> iter = sortedParamMap.entrySet()
    .iterator();

  while (iter.hasNext()) {
   Map.Entry<String, String> kvpair = iter.next();
   buffer.append(percentEncodeRfc3986(kvpair.getKey()));
   buffer.append("=");
   buffer.append(percentEncodeRfc3986(kvpair.getValue()));
   if (iter.hasNext()) {
    buffer.append("&");
   }
  }
  String cannoical = buffer.toString();
  return cannoical;
 }

 private String percentEncodeRfc3986(String s) {
  String out;
  try {
   out = URLEncoder.encode(s, UTF8_CHARSET).replace("+", "%20")
     .replace("*", "%2A").replace("%7E", "~");
  } catch (UnsupportedEncodingException e) {
   out = s;
  }
  return out;
 }

}
 
15:29

Product Advertising APIをRestでアクセスする

Category: , By jx
AmazonアソシエイトWebサービスの名称が、「Product Advertising API」に変更された。これに伴って、今まで使用していたアクセス方法は使用出来なくなり、リクエスト自体にサインをしなければいけなくなった。 ライブラリが出てくるまで、ちょっとこの変更はつらいかも。ということで、そのRestでのアクセス方法を説明する。英語ですが、Amazonのページにちゃんと書かれています。 今までのリクエスト、例えば、ItemLookupは以下のようなリクエスト
http://ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=0000000000000000&AssociateTag=jx-22&ItemId=4877712399&Operation=ItemLookup&ResponseGroup=Medium&Version=2008-08-19
これに、タイムスタンプをつけます。次の例では、GMTのタイムスタンプをつけます。2009-0509T06:30:41Z
http://ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=0000000000000000&AssociateTag=jx-22&ItemId=4877712399&Operation=ItemLookup&ResponseGroup=Medium&Version=2008-08-19&Timestamp=2009-05-09T06:20:41Z
このリクエストをURLエンコードします。RFC3986に従って、エンコードします。
http://ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=0000000000000000&AssociateTag=jx-22&ItemId=4877712399&Operation=ItemLookup&ResponseGroup=Medium&Version=2008-08-19&Timestamp=2009-05-09T06%3A20%3A41Z
パラメータのキーと値のペアを抜き出します。
Service=AWSECommerceService AWSAccessKeyId=0000000000000000 AssociateTag=jx-22 ItemId=4877712399 Operation=ItemLookup ResponseGroup=Medium Version=2008-08-19 Timestamp=2009-05-09T06%3A20%3A41Z
さらに、それをソートします。ソートするときは、文字コードの値でソートします。つまり、Aよりも、aの方が後に来ます。
AWSAccessKeyId=0000000000000000 AssociateTag=jx-22 ItemId=4877712399 Operation=ItemLookup ResponseGroup=Medium Service=AWSECommerceService Timestamp=2009-05-09T06%3A20%3A41Z Version=2008-08-19
ソート済みのこれらの文字列をアンパサンドで結合します。
AWSAccessKeyId=0000000000000000&AssociateTag=jx-22&ItemId=4877712399&Operation=ItemLookup&ResponseGroup=Medium&Service=AWSECommerceService& Timestamp=2009-05-09T06%3A20%3A41Z&Version=2008-08-19
さらに、下の文字列を用意します。改行文字も必要です。
GET ecs.amazonaws.jp /onca/xml
このサインする文字列を以下のようになります。
GET ecs.amazonaws.jp /onca/xml AWSAccessKeyId=0000000000000000&AssociateTag=jx-22&ItemId=4877712399&Operation=ItemLookup&ResponseGroup=Medium&Service=AWSECommerceService& Timestamp=2009-05-09T06%3A20%3A41Z&Version=2008-08-19
Secret Access Keyの文字列を利用して、SHA256ハッシュアルゴリズムを使用するRFC2104-compliant HMACを適用します。
pwqYQRc3RepIrf7m+VMRy/jFXx/ZBSPsaSFFexIUoSI=
この文字列をURLエンコードします。
pwqYQRc3RepIrf7m%2BVMRy%2FjFXx%2FZBSPsaSFFexIUoSI%3D
最後に、先ほど生成したURLにこのSignatureのキーで追加します。
AWSAccessKeyId=0000000000000000&AssociateTag=jx-22&ItemId=4877712399&Operation=ItemLookup&ResponseGroup=Medium&Service=AWSECommerceService& Timestamp=2009-05-09T06%3A20%3A41Z&Version=2008-08-19&Signature=pwqYQRc3RepIrf7m%2BVMRy%2FjFXx%2FZBSPsaSFFexIUoSI%3D
これで、リクエストが完成です。このJava実装は、Amazonのページに記載されています。それは、日本用にはなっていないので、それを日本用のものに変更したものを次のエントリーで掲載します。
 
21:41

AWSのキャッシュの制限は厳しい

Category: By jx
AWS Customer Agreementの5.1.12によれば、AmazonWebServiceの以下の項目は1ヶ月キャッシュしても良いとのこと。
URL ASIN Product Name Catalog
Artists Authors MPN Starring
ISBN Directors Manufacturer Media
Distributor Release Date Publisher Num Media
UPC Reading Level Theatrical Release Date Platforms
MPAA Rating ESRB Rating Age Group Encoding
逆にこれに無いような情報は、24時間しかキャッシュしてはいけないそうなんです。期限が切れる毎に、すごいリクエストが発生しそうなんだけど、文句言われないかが心配。 にしても、awsのレスポンスはGoogleの検索並みに早い。すごいと思う。どんな仕組みなんだろ。memcached等の分散キャッシュサーバを使用すれば、こういったことが可能になるんだろうか。。。
 
16:54

AmazonのWSDLからJavaのクライアントの生成

Category: By jx
いつも思うのだけれど、WSDLで生成されたソースコードは激しくわかりづらい。生成してもなかなか使い方がわかりにくいから嫌い。Amazonももちろん使いづらくてどうしたら良いのかよくわからなかった。試行錯誤の上やっとできるように。 まず、WSDLの場所なんだけれども、英語ページを見ていると、上のURLにたどり着くが、これだと日本の商品にアクセス出来ない。ちゃんと調べると、下のURLが正しいことがわかった。 http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl http://webservices.amazon.com/AWSECommerceService/JP/AWSECommerceService.wsdl そしたら、以下のコマンドを実行して、スタブを生成
wsimport http://webservices.amazon.com/AWSECommerceService/JP/AWSECommerceService.wsdl
で、まぁここまでは、簡単なんだけど、その使い方が難しい。自由区/Webサービス紹介/Amazon E-Commerce Service - DoboWikiで、.Net用みたいだけど、説明してくれているのが役に立つ。で、できたのが下のソースコードでItemLookupのサンプルになってる。 ん〜自分で作ったRESTクライアントの方が早いし、わかりやすいからそのままにしようか。。。。
String marketplaceDomain = null;
String awsAccessKeyId = "19A48A3RYVJ128FSEN82";
String subscriptionId = null;
String associateTag = "jx-22";
String validate = null;
String xmlEscaping = null;
ItemLookupRequest shared = null;
List<ItemLookupRequest> request = new ArrayList<ItemLookupRequest>();
ItemLookupRequest req1 = new ItemLookupRequest();
req1.getItemId().add("4990412621");
req1.getItemId().add("4835612582");
request.add(req1);
Holder<OperationRequest> operationRequest = null;
Holder<List<Items>> items = new Holder<List<Items>>();

AWSECommerceService client = new AWSECommerceService();
AWSECommerceServicePortType service = client
        .getAWSECommerceServicePort();
service.itemLookup(marketplaceDomain, awsAccessKeyId, subscriptionId,
        associateTag, validate, xmlEscaping, shared, request,
        operationRequest, items);
List<Item> itemList = items.value.get(0).getItem();
for (Item item : itemList) {
    System.out.println(item.getItemAttributes().getTitle());
}