The net is vast
プログラミングや、コンピュータなどの備忘録です。 主にRuby, Java, Linux, 等を扱います。アルゴリズムも扱いたいな。
0:44

fastlookup_bookmarklet ブックマークレットで辞書を引く

Category: By jx

fastlookup alc なんかがあると、辞書を引くのが簡単です。でも、userscriptが使えないとだめだったりして、最近はもっぱらChromeを使用しているため、使えません。仕方ないので、YAHOO Pipesを利用して、YAHOOの英和辞書を引くブックマークレットを作成しました。自分はIEを使わないので、IEには非対応ですが、簡単に対応できるはずです。

必要なときだけブックマークレットを読みこめばいいし、使い勝手は悪くないと思います。

使い方は簡単。ブックマークレットを実行したら、辞書を引きたい単語を選択してください。Firefox, Webkit系で動くはずです。

fastlookup_bookmarklet

 
0:12

Dijkstra's AlgorithmとA-Star AlgorithmをJavaScriptで実装してみた

Category: , By jx

ダイクストラ法とA-StarアルゴリズムをJavaScriptで実装してみた。

ダイクストラ法は確かに動いているように思えるけど、A-Starの方はうまく動いていないように見えるというか、全然高速化されていない。こんなもんなのかな?知ってる人いたら教えてください。

Create Map:
(function() {
var start;
var tdList;
var mapSize = 10;
var map; 
var dijkstra = function(sx, sy, gx, gy, astar) {
 var startTime = new Date().getTime();
 var masterList = new NodeList(); // Node全てを格納するリスト
 var openList = new NodeList(); // スコアが確定していないノードを格納するリスト
 var closeList = new NodeList(); // スコアが確定したノードを格納するリスト
 var maxWidth = map[0].length; // 地図の幅
 var maxHeight = map.length; // 地図の高さ

 message(''); // ログ
 // 地図データからノードを作成
 for (var i = 0, len = map.length; i < len; i++) {
  for (var j = 0, jlen = map[i].length; j < jlen; j++) {
   masterList.add(new Node(j, i));
  }
 }

 // スタートノードの設定
 var targetNode = masterList.get(sx, sy);
 targetNode.score = 0;
 closeList.add(targetNode); // スタートノードはcloseListに格納しておく

 var whileCount = 0;
 while (true) {
  whileCount++;
  // 次のノードを探索
  var checkNodePositions = [[targetNode.x, targetNode.y - 1], [targetNode.x, targetNode.y + 1], [targetNode.x - 1, targetNode.y], [targetNode.x + 1, targetNode.y]];
  for (var i = 0, len = checkNodePositions.length; i < len; i++) {
   var checkNodePosition = checkNodePositions[i];
   // 地図の範囲外であれば探索しない
   if (checkNodePosition[0] < 0 || checkNodePosition[0] > maxWidth - 1 ||
     checkNodePosition[1] < 0 || checkNodePosition[1] > maxHeight - 1) {
    continue;
   }
   var checkNode = masterList.get(checkNodePosition[0], checkNodePosition[1]);
   if (checkNode.type == 0) continue; // 壁だったら処理しない
   if (closeList.get(checkNodePosition[0], checkNodePosition[1])) continue; // スコアが確定していればなにもしない
   if (!openList.get(checkNodePosition[0], checkNodePosition[1])) {
    openList.add(checkNode); // openListにもcloseListにも無ければopenListに格納しておく
   }
   var score;
   if (astar) {
    score = targetNode.score + 1 + Math.abs(checkNodePosition[0] - gx) + Math.abs(checkNodePosition[1] - gy);
    //console.log(score);
   } else {
    score = targetNode.score + 1; // この経路を辿ったときのスコアはtargetNode.score + 1
    //console.log(score);
   }
   // スコアが格納されていない、またはほかの経路よりも効率が良ければスコアと、一つ前のノードをセットする
   if (isNaN(checkNode.score) || checkNode.score > score) {
    checkNode.score = score;
    checkNode.parent = targetNode;
   }
  }
  // openListが0になってしまったら、辿ることができないということ
  if (openList.length() == 0) {
   message('No route!');
   return;
  }
  // 現在openListに格納されている最小のスコアのノードはスコアを確定し、closeListに格納する
  targetNode = openList.getMin();
  openList.remove(targetNode);
  closeList.add(targetNode);
  targetNode.score = targetNode.score;
  if (targetNode.x == gx && targetNode.y == gy) break;
 }
 var endTime = new Date().getTime();
 message(endTime - startTime + 'ms' + ' whileCount:' + whileCount);
 showRoute(targetNode); // ルートを表示する
}
var message = function(value) {
 document.getElementById('message').innerHTML = value;
}
var showRoute = function(targetNode) {
 var td = tdList.get(targetNode.x, targetNode.y);
 td.className = 'scotch route';
 td.style.backgroundColor = 'green';
 var parent = targetNode.parent;
 if (parent) showRoute(parent);
}
var createMapData = function(count) {
 map = [];
 start = [0, 0];
 for (var i = 0, len = mapSize * count; i < len; i++) {
  var row = [];
  for (var j = 0, jlen = mapSize * count; j < jlen; j++) {
   var r = Math.floor(Math.random() * 100) % 5;
   row.push(r);
  }
  map.push(row);
 }
}
var createMap = function() {
 var graph = document.getElementById('graph');
 graph.innerHTML = '';
 var table = graph.appendChild(document.createElement('table'));
 table.style.borderCollapse = 'collapse';
 var tbody = table.appendChild(document.createElement('tbody'));
 tdList = new Table();

 for (var i = 0, len = map.length; i < len; i++) {
  var tr = tbody.appendChild(document.createElement('tr'));
  var row = [];
  for (var j = 0, jlen = map[i].length; j < jlen; j++) {
   var td = tr.appendChild(document.createElement('td'));
   observe(td, 'click', analyse);
   td.style.border = '1px solid white';
   td.style.width = '10px';
   td.style.height = '10px';
   if (map[i][j] == 0) {
    td.className = 'scotch wall';
    td.style.backgroundColor = 'red';
   }
   else td.className = 'scotch';
   tdList.set(j, i, td);
  }
 }
}
var analyse = function(event) {
 var target = event.target || event.srcElement;
 var trChildren = target.parentNode.children;
 var tbodyChildren = target.parentNode.parentNode.children;
 var x,y;
 for (var i = 0, len = trChildren.length; i < len; i++) {
  if (trChildren[i] == target) {
   x = i;
   break;
  }
 }
 var tr = target.parentNode;
 for (var i = 0, len = tbodyChildren.length; i < len; i++) {
  if (tbodyChildren[i] == tr) {
   y = i;
   break;
  }
 }
 if (start[0] == x && start[1] == y) {
  message('Start is equals to Goal');
  return;
 }
 if (target.className.indexOf('wall') != -1) {
  message('Your clicked cell is wall');
  return;
 }
 // routeをクリアする
 tdList.each(function(td) {
  var className = td.className;
  var classNameList = className.split(/ /);
  for (var i = classNameList.length - 1; i >= 0; i--) {
   if (classNameList[i] == 'route') {
    classNameList.splice(i, 1);
    td.style.backgroundColor = '';
   }
  }
  td.className = classNameList.join(' ');
 });
 var astar = document.getElementById('astar20090824').checked;
 dijkstra(start[0], start[1], x, y, astar);
 start = [x, y];
}
var observe = function(target, type, listener) {
 if (target.addEventListener) target.addEventListener(type, listener, false);
 else target.attachEvent('on' + type, function() { listener.call(target, window.event); });
}
var Node = function(x, y) {
 this.x = x;
 this.y = y;
 this.type = map[y][x];
}
Node.prototype.score = NaN;
Node.prototype.parent = null;

var NodeList = function() {
 this.data = {};
}
NodeList.prototype.add = function(value) { this.data[value.x + ',' + value.y] = value; }
NodeList.prototype.get = function(x, y) { return this.data[x + ',' + y]; }
NodeList.prototype.length = function() {
 var count = 0;
 for (var key in this.data) {
  count++;
 }
 return count;
}
NodeList.prototype.remove = function(value) { delete this.data[value.x + ',' + value.y]; }
NodeList.prototype.getMin = function() {
 var data = this.data;
 var min = null;
 for (var node in data) {
  if (!min) {
   min = data[node];
   continue;
  }
  if (min.score > node.score) min = data[node];
 }
 return min;
}
var Table = function() {
 this.data = {};
}
Table.prototype.set = function(x, y, value) { this.data[x + ',' + y] = value; }
Table.prototype.get = function(x, y) { return this.data[x + ',' + y]; }
Table.prototype.each = function(func) {
 var data = this.data;
 for (var key in data) {
  func(data[key]);
 }
}

observe(document.getElementById('mapSelect20090824'), 'change', function(event) {
 var target = event.target || event.srcElement;
 var value = target.value;
 createMapData(value);
 createMap();
});
createMapData(1);
createMap();
})();
 
14:00

XenServerのインストールPDFがすばらしい

Category: By jx
XenServerが無償化されているので、全てのサーバリソースをXenServerにしてみようかとインストールしてみた。XenServerのインストールに関してエントリーを書こうと思ったら本家の日本語サイトCitrix XenServer 5.0 無償版インストール Step by Step Guide(PDF)が置いてあった。 これを見てみると、XenServerを動かすまでの全ての手順が載っているので、大変わかりやすい。まぁXenServerは動かすのが簡単なので、ドキュメントもいらないくらい。インストールには30分もかからないほど簡単。 XenServerでOSを動かせば、マシンを買い替えてハードウェアが変わっても、マシンイメージを異動すればそれで環境移行が完了してしまうので楽。さらに、ライブマイグレーションを使えば、Xen上で動いているマシンをほかの物理マシンに動作しているまま移行させることができる。これはすばらしすぎる。 自宅サーバで運用していると人は是非XenServerを考えてみてはいかがだろう。
 
23:08

gem 1.1から1.3へのアップデートではまる

Category: By jx
ずいぶん前にRailsをやってから、しばらく触ってなかったので、gemが1.1のままでした。NetBeansでRailsプロジェクトを作るとGemが1.3.1以上じゃないとダメだよとおこられてしまったので、Gemをアップデートしようとすると、
$ sudo gem update --system Updating RubyGems Bulk updating Gem source index for: http://gems.rubyforge.org/ Nothing to update
といわれて、アップデート出来ない。 どうしたら良いのかなと思って調べていたらgem update --system で失敗する場合にはという記事を発見。以下のコマンドでアップデート完了です。
$ sudo gem install rubygems-update $ sudo update_rubygems
 
23:37

紙の本が100%亡くなることは無い

Category: By jx
紙の本が100%亡くなると断言できる、たった一つの理由
断言できる理由、それは紙の本が"印刷"という技術だからです。そして、「紙の本に限ってそれは無い」と錯覚しやすいのは、「紙に印刷する」という技術があまりの大発明であったからです。あまりに長く親しまれすぎて、それが技術だと認識できないのです。 技術と言うのは、必ずそれを上回るまったく新しい技術によって取って代わられる運命にあります。
何死ぬほど眠たい事言ってるんでしょうか。一般層への普及の条件というものをはき違えています。印刷という技術が普及したんじゃないんですよ、印刷したものが便利だったから普及したんです。技術だから新しいものにとって代わられるんじゃない。使いやすいからみんながそっちを使うんです。 既存の技術が新しい技術に取って代わられるというのは、「同じこと」を実現するのに、より性能の良いもの、コストの安いものの時に成り立つことです。今回の比較はインタフェースが異なってしまいます。使い勝手違います。もし、kindleのような電子ブックリーダーを想定してそのようなことを言っているとしたら、お門違いも甚だしいです。
"音"を記録する音楽メディアは、この100年ほどの間にレコード→カセット→CDと変遷してきました。そして今はCD→ダウンロード、となる過渡期です。
この例では音楽を挙げていますが、この媒体の目的は音楽を聴くことが目的で、レコード、カセット、CD、ダウンロード、どれもイヤホン(スピーカー)を通して聴きます。インタフェースが変わっていないのです。インタフェースが変わらないのであれば、より小型、よりポータブルなものがより利便性が高いことは間違い在りません。だから、音楽という媒体は、この変遷を辿ったのです。 紙の本は状況がことなります。電子ブックリーダーという媒体を考えるのであれば、やはりインタフェースとして非力です。書き込めないし、次のページへの移動が、ページ遷移となってしまって連続しなくなってしまいますし、どれだけめくったのが実感できなかったり。 紙という媒体以上に便利なものを想像できますか?そもそも、人間が想像出来ないものは作ることはできません。もちろん、これから検索出来るという利便性から電子媒体のものは増えていくでしょう。しかし、やはりそれは一部の機械好きの人たちのガジェットに過ぎず、紙という媒体の代わりにはなりえません。 技術は生活をより便利にするものです。より便利になるものであれば古いものに取って代わりますし、そうでなければ変わることはできないでしょう。そして、人間は想像すらできないものは作ることができません。
 
1:37

GAE/JでXMLを操る

Category: By jx
GAE/Jで開発をしていると、どうしてもXMLを操らなきゃいけないときがあります。開発機でRomeを使って動いたぞと思って本番環境にデプロイしても動作しません。下のような例外が発生してしまいます。
Could not load default SAX parser
なんて言われてしまいます。これを解決するにはxercesの次のjarが必要になります。
  • serializer.jar
  • xercesImpl.ja
  • xercesSamples.jar
  • xml-apis.jar
これでも実はまだ足りません。XPathを使おうと思っても、xercesにはXPathのAPIは用意されておらず、
XPathFactory.newInstance()
上のようにするとやはり例外が発生してしまいます。
XPathFactoryConfigurationException: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFactory implementation found for the object model: http://java.sun.com/jaxp/xpath/dom
これを解決するには、xalanが必要になります。 結局、xalanにはxercesが含まれている?ので結局、xalan-j_2_7_1の以下のjarをWEB-INF/libの下に配置します。
  • serializer.jar
  • xalan.jar
  • xercesImpl.jar
  • xml-apis.jar
  • xsltc.jar
さらに、
XPathFactory.newInstance()
のように書いてもダメで、次のように書かなければ行けません。
new org.apache.xpath.jaxp.XPathFactoryImpl();
まとめると、
  1. Romeを使用して
  2. xalanのjarを使って
  3. new org.apache.xpath.jaxp.XPathFactoryImpl()で、Factoryを作成します
 
19:00

「はまらない」ような体制を作ろう

Category: By jx
小野和俊のブログ:プログラマーの開発速度は「はまる」時間の長さで決まる 「はまる」時間で開発速度が決まるのであれば、そもそも「はまらない」ようにすればいい。そういう体制を作ろう。 周りがいつもハマっていたりする原因は、その使用するフレームワークや技術を誰も知らないとき。だったら、そんなもの使わずに、わかっているものを使えば良い。特に最近の重厚なフレームワークなんかを使った場合には、つぎはぎでつないできたプロジェクトが、最後の方でにっちもさっちもいかなくなってしまうようなことが多い。 上のように考えても、新しい技術やフレームワークを使うメリットが在ると感じるならば、わかっている人をつれてこよう。開発することが決まってから、自分たちだけで新しい技術を見極めるなんて並たいていの人ができることじゃない。 コーダーとしては、小野和俊さんの言う通りのことを気をつければ良いと思う。ただ、 そのパフォーマンスを決めてしまうのは、プロジェクトの方針を決めてしまう人だから、重要なフェーズでは気をつけて判断してほしい。
 
2:06

青学大ももうダメかもね

Category: By jx
青学大「iPhone」無料配布…狙いは「代返防止」
記者会見した伊藤定良学長は「次世代の携帯端末を活用し、人材を育成していきたい」と話した。
これ、本気で言ってるの?iPhoneの本当の目的が「自在を育成していきたい」ということであれば、勉強というものの本質をわかってない。授業に出席しさえすれば、学力が向上するとでも思ってるんだろうか。 学力が低下したのは、出席点というなにを評価しているのかわからない指標で生徒を評価しているから。学力がどの程度かはかりたいのであれば、純粋に学力をはかればいい。なんで、出席することのなにが学力向上においてい重要なんだろうか。 こういった直接的でない指標で学生を評価していれば、もちろん学生は評価されるように頑張る。出席点をかせげばあまり学力が無くても、卒業出来るのであればそちらの方がらくなんだからそっちに流れるのは当たり前。人間楽な方に走りたくなるからね。学力はなくても、なんか出席してればいいらしいよ〜という話になる。 つまり、学力をつけさせたいのなら、出席点なんてクソなものは捨ててテストのみの結果で判断すればいい。大学というのはそもそも、そこで学力を身につける場所でしょう。授業に出ていなくても最初からその学力があるのであれば、無駄な時間を授業に割かせずに、さらにその上の学力なり人生経験を積めるように時間を与えてあげるという考え方はできないのか。
 
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());
}
 
20:16

GAE/Jでparentは変更出来ないけど、childは追加可能

Category: By jx
タイトルのまんま。
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
 Parent parent = pm.getObjectById(Parent.class, "hoge");
 List childList = parent.getChildList();
 int r = (int) Math.floor(Math.random() * 100000);
 childList.add(new Child(KeyFactory.createKey(Child.class
   .getSimpleName(), "fuga" + r)).setName("fuga" + r));
 
} finally {
 pm.close();
}
もちろん、削除もできる。
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
 Parent parent = pm.getObjectById(Parent.class, "hoge");
 List childList = parent.getChildList();
 Child child = childList.get(0);
 pm.deletePersistent(child);
} finally {
 pm.close();
}
 
19:51

GAE/Jのparentとchildは変更出来ない?

Category: By jx
一度作成したオブジェクトをほか子供にしようとしたら
try {
 Parent p = new Parent("hoge");
 Key cKey = KeyFactory.createKey(
  Child.class.getSimpleName(), "fuga");
 Child c = new Child(cKey);
 pm.makePersistent(c);
 pm.makePersistent(p);
 p.setChild(c);
} finally {
 pm.close();
}
Detected attempt to establish Parent(hoge) as the parent of Child(fuga) but the entity identified by Child(fuga) has already been persisted without a parent. A parent cannot be established or changed once an object has been persisted.
と言われてしまった。つまり、一度parentを設定してしまったら、変更は不可能の模様。これって要するに1レコードでデータが格納されるということなんだろうか。 なかなか難しいですな
 
2:59

GAE/JavaのQueryでは!=演算子はサポートされない

Category: By jx
GAEのQueryを触っていてハマった。
Query pageQuery = pm.newQuery(Page.class);
pageQuery.setFilter("body != null");
SQLの気分で、上のように書いていたら、エラーがでてしまう。
App Engine datastore does not support operator <>
ってでてしまう。すごいハマった。ドキュメントをちゃんと読んでみたら、jdoqlでは!=演算子はサポートされないらしい。ちゃんと書いてあるじゃないですか。
Note: The Java datastore interface does not support the != and IN filter operators that are implemented in the Python datastore interface. (In the Python interface, these operators are implemented in the client-side libraries as multiple datastore queries; they are not features of the datastore itself.)
 
0:24

GAE/JavaのJDOでPrimaryKeyをStringにすると厄介

Category: By jx
GAE/JavaのJDOでPrimaryKeyをStringにすると厄介です。
@PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private String primaryKey;
こんな風にすると、primaryKeyは数字で始まることができないようです。
Name may not start with a digit
というようにおこられてしまいます。 数値で始まるPrimaryKeyを主キーにしたい場合にはどうしたら良いんでしょう。というかまだ、JDOが全然わかってません。誰か、あのドキュメントを翻訳して。。。
 
23:31

集合知プログラミング第11章 進化する知性

 
23:20

集合知プログラミング第9章 高度な分類手法:カーネルメソッドとSVM

 
0:54

集合知プログラミング第7章 決定木によるモデリング

例によって、勉強したので公開
 
0:03

集合知プログラミング第5章 最適化

とりあえず勉強会用資料を作成した。基本的に自分が勉強するための資料なので、うまくまとまってもいないけど公開します
 
2:29

集合知プログラミング第3章のドラフト

途中だけど、とりあえず外から見えるようにしておく。
 
16:53

Mac PortsでのMysql5のインストール

By jx
忘れないようにメモしておく。 $ sudo port install mysql +server $ sudo chown -R mysql:mysql /opt/local/var/db $ sudo -u mysql mysql_install_db5