2010-12-31

ロリポップの MySQL を Perl の CGI で使う

ロリポップ、MySQL が使えるらしいけれど、
データベースについては基本的にサポート外とさせていただきます。
詳しい知識がある方のみご利用ください。
とのことで詳細わからず、やってみてできたことだけ、書いておこうと思った次第。

ロリポップでの設定

ロリポップにログインして、ユーザー専用ページで「WEB ツール>データベース」を開く。 「データベース作成」ボタンを押す。

ロリポップの MySQL データベース設定

次の画面で、データベースのサーバーを選択、データベース名、パスワードを入力して「作成」ボタンを押す。

ロリポップの MySQL データベース設定

すると、こんな画面になって、ロリポップ管理画面での作業はおしまい。管理ツール phpMyAdmin を使って、テーブルとかをここで作ってしまうのもありかと。

ロリポップの MySQL データベース設定

Perl プログラム

あとは、サーバー上の Perl プログラムの書き方。参考にしたのはこちら。 で、例えば既存の list テーブルに一行挿入するプログラムがこちら。

use DBI;

$db = DBI->connect('DBI:mysql:LA00000000-abcdefghijkl:mysql504.phy.lolipop.jp', 'LA00000000', '1234567890123456');
$sth = $db->prepare("INSERT INTO list VALUES (1,'1st','memo 1st')");
$sth->execute;
$sth->finish;
$db->disconnect;

赤はデータベース名、青はデータベースのサーバー名、緑はユーザー名、紫はパスワード。それぞれ、先ほどロリポップのデータベース作成で設定した値を使用。

もひとつ、list テーブルから id、name、memo を取得して表示するプログラムがこちら。

use DBI;

$db = DBI->connect('DBI:mysql:LA00000000-abcdefghijkl:mysql504.phy.lolipop.jp', 'LA00000000', '1234567890123456');
$sth = $db->prepare("SELECT id, name, memo FROM list");
$sth->execute;
$num_rows = $sth->rows;
print "該当 $num_rows 件\n";
for ($i=0; $i<$num_rows; $i++) {
  @a = $sth->fetchrow_array;
  print "id=$a[0], name=$a[1] memo=$a[2] \n";
}
$sth->finish;
$db->disconnect;

お、なんだか自分でも色々できそうな気がしてきたぞ!というところで、おしまい。何かちゃんとしたもの、作ってみて公開するかも。

2010-12-30

Windows XP で My Pictures をマイピクチャに戻す方法

Windows XP で、いつの間にか「マイピクチャ」フォルダが「My Pictures」に変わっていたという話。どちらにせよフォルダの実体は「My Pictures」なので、そのままでもいいかなと思ったものの、「マイミュージック」と表記が合わなくなってしまうなと思い直し、復旧の方法を探る。

すると、こちらを発見。 この記事によると、コマンドプロンプトで、
C:\Documents and Settings\user>rundll32 mydocs.dll,PerUserInit
とするといいらしい。もちろん、赤字部分は自分のユーザー名。このとおりやってみると、すぐに解決。ありがたやー。

2010-12-25

Windows 7 がネットワークにつながらない…

Windows 7 のノートパソコンが、急にネットワークにつながらなくなって、途方にくれる。

コントロールパネル「ネットワークと共有センター」を見てみると、「アクティブなネットワークの表示」欄になぜか 2 つの値が…。つまり、コンピュータが 2 つのネットワークに属していることになっているみたい。

コマンドプロンプトから ipconfig してみると、やっぱりデフォルトゲートウェイが 2 つになっていた。

しばらく色々いじって、ウンウンうなっていたのだけれど、結局 Google 先生のお世話になることに…。別のパソコンで調べて、最終的にたどり着いたのがこのページ。
ネットワーク宛先0.0.0.0、ネットマスク0.0.0.0のテーブルを削除
>route delete 0.0.0.0 mask 0.0.0.0
OK!

ネットワーク宛先0.0.0.0、ネットマスク0.0.0.0、ゲートウェイ192.168.146.2のテーブルを追加
>route add 0.0.0.0 mask 0.0.0.0 192.168.146.2
OK!
これでバッチリ直りました。ありがたやー。あ、もちろん、赤字部分は自分のネットワークのゲートウェイのアドレスで。

あと、route delete や route add するコマンドプロンプトも「スタート>すべてのプログラム>アクセサリ」の「コマンドプロンプト」を右クリック「管理者として実行」で開かないと、

>route delete 0.0.0.0 mask 0.0.0.0
要求された操作には管理者特権が必要です。

って言われちゃうので、注意がいるかも。

2010-12-12

Firefox で画像の幅と高さをコピーするためのブックマークレット

ブログに画像を載せる時、
<img src="http://example.com/image.jpg" />
と書いたあと、実際にそのページを Firefox で表示してみて、

画像の右クリックから「画像だけを表示」、Firefox のタイトルバーに表示されている幅と高さを img の width と height の要素に転記する…というようなことをしてきたのだけれど、転記の際にけっこう忘れてしまうので、ブックマークレットにしてみた。



WxH

需要があればどうぞ。

2010-12-05

Google App Engine 1.4.0 へのアップデートではまる…。

というわけで、1.4.0 SDK へ更新しようとした矢先、eclipse のコンソールにこんなメッセージが…。
インストールする項目の収集中にエラーが発生しました
  No repository found containing: org.eclipse.jst.server.core/osgi.bundle/1.2.0.v20090421
  No repository found containing: org.eclipse.wst.server.core/osgi.bundle/1.1.102.v20090825
大分アップデートしていなかったから、なんかほかのを先にしなくちゃいけないのかな、と色々更新してからもう一度チャレンジしてみたら…、
インストールする項目の収集中にエラーが発生しました
  No repository found containing: com.google.appengine.eclipse.core/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.core/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.platform/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.platform.e34/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.platform.shared/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gdt.eclipse.suite/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gwt.eclipse.core/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gwt.eclipse.oophm/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.appengine.eclipse.sdkbundle.1.4.0/osgi.bundle/1.4.0.v201012021501
  No repository found containing: com.google.gdt.eclipse.maven/osgi.bundle/1.4.0.v201010280047
  No repository found containing: com.google.gwt.eclipse.sdkbundle.2.1.0/osgi.bundle/2.1.0.v201010280047
ふ、増えてるし…。

ということで、結局 Google 先生のお世話になることにして、こんなページを発見。 書かれているのとは少し違う方法だけれど、eclipse で「ヘルプ > ソフトウェア更新 > サイトの管理」とすすみ、「エクスポート」で bookmarks.xml を保存。

全部のサイトを選択後「除去」して、さっきのファイルを「インポート」…、っていう風にするとできましたとさ。めでたし、めでたし。

2010-11-07

JQuery のプラグイン Tablesorter が便利な件

JQuery でいろんなプラグインを試しているところ。
で、発見したのが各項目でソートできるテーブル「Tablesorter」。
NoタイトルURL
1クリボウの Blogger Tipshttp://www.kuribo.info/
2クリボウの写真日記http://kuribo-photo.blogspot.com/
3クリボウのプログラミングひとりごとhttp://kuribo-programming.blogspot.com/
4Japanese Bloggers Infohttp://japanese-bloggers.appspot.com/

HTML で記述するテーブルには thead、th、tbody と class="tablesorter" が必須。
JQuery のコードと Tablesorter のコードスタイルシートを読み込ませて、
$(function() {
  $("#myTable").tablesorter();
});
とするだけ。

非常に便利なので、Japanese Bloggers Info でも、登録ブログ一覧のリストに使ってみたところ。
とはいえ表示させる件数が多いとやっぱり…重いね。

2010-11-03

YouTube 動画がモヤモヤして見られないとき

YouTube 動画がモヤモヤしてちゃんと再生できないとき、



画面上の右クリックで Adobe Flash Player のメニューを出して、



「設定 > ディスプレイ」の「ハードウェアアクセラレーションを有効化」のチェックをはずしてみると、



幸せになれるかも。

2010-10-31

JQuery はじめました

#ajnk3 で刺激を受けて、新しいことに挑戦しようと思っているところ。で、昨日からひとまず JQuery を始めてみた。

とっかかりは、JQuery のすすめ的な、こちら。
CSS と JavaScript がちょっとできれば、何とかなりそうな感じ。
それで、次はこれを見て色々コードを打ってみる。 あとは、以下のリファレンスを見ながら、 あーでもないこーでもないとパソコンに向かっていたら、一通り、AJAX 的なページができた。非常に簡単+便利なので、これまでの食わず嫌いを反省中。来週は JQuery UI と組み合わせて使ってみよっと。

今回作った JQuery を使ったページは、Japanese Bloggers Info の、登録 Blogger ブログの投稿データからトラックバックを送るページに使用。今途中のトラックバックを受け取る方の仕組みを完成させたら、公開する予定。

できなかったことができるようになるっていうのは、やっぱりいくつになってもうれしい。

2010-10-15

TaskQueue が Lab 卒業へ

TaskQueueが11月にLabを卒業するとな。 http://ow.ly/2Th9p と同時にクオータ制限が掛かるそうだ。 #appengineless than a minute ago via HootSuite


まだ Lab から出てなかったんだという驚きもありつつ、「Task Queue API Calls」に「Task Queue Stored Task Count」、「Task Queue Stored Task Bytes」の 2 つの Quota が加わるというのは、やはり痛手に感じたり。 今ですらこんなになっているときがあるというのに…、大丈夫なんだろうか…。

Tasks Strage Quota

2010-09-26

「小悪魔女子大生のサーバエンジニア日記」がかわいすぎ

何これ、かわいい…。
ゆるーい感じのイラストが楽しい、
女子大生が書いているネットワークについてのブログ。

追記(2011-02-19):

あ、本になったんですね。これ一冊欲しいかも。

2010-09-11

Google Chrome で RefControl?

メインのパソコンではいつもブラウザに Firefox を使っているのだけれど、新しいノートパソコンでは Chrome へ浮気中。で、Chrome 用プラグインを色々物色しているところ。

それで Chrome でも livedoor Reader で FC2 ブログとかの画像を見たいなと、Firefox の RefControl 的なプラグインを探し回っていたところ、こんなものを発見。

livedoor Reader と Fastladder で実行されるユーザースクリプトで、読み込みエラーの出た画像をリファラなしで再読み込みしてくれるというスグレモノ。エラー検知→再読み込みという手順を踏むのでやや遅いけれど、まあそんなに気にならない。

インストールは、Chrome で gist: 281516 - GitHub の「raw」リンクをクリック、「続行」「インストール」とボタンをクリックしていくだけ。

Firefox しかできないと思っていた色んなことが徐々に Chrome でもできると分かってきて、メインパソコンでも Firefox を使い続けるのかどうか、少し考え始めてきちゃったり…。ううむ。

2010-09-05

acer Aspire TimelineX 3820T 買った



安いノート PC をと、acer Aspire TimelineX 3820TAmazon で購入。ぎりぎり持ち運びの可能な 13.3 型ワイドディスプレイで、Intel Core i5-450M。驚くほどサクサク動いてくれるので、Value Star(AMD Athlon XP 2500+)のメインの座が一気に吹き飛んでしまった感。ううむ。

acer Aspire TimelineX 3820T Windows エクスペリエンスインデックス

で、これが TimelineX 3820T の Windows エクスペリエンスインデックス。

プロセッサ:6.8、メモリ:5.5、グラフィックス:4.0、ゲーム用グラフィックス:5.1、プライマリーハードディスク:5.8。

って、なかなかいいんじゃないでしょうか。グラフィックが Intel HD Graphics というので少し心配していたけれど、許容範囲で一安心。



実は、ノート PC を所有すること自体が初体験なのだけれど、初体験ついでに無線 LAN ルータと Bluetooth マウスも購入。マウスの灰色がなーと思っていたけど、届いてみるとつや消しの明るい感じの銀。うれしい誤算で、黒と銀のなかなか渋くていい感じ。

あと、新しいマウスパッドとかイヤホンとか買ったけれど、それはまあ割愛。…ということで、3820T を持ってどこへ行こうかと、ワクワクしている今日この頃。

2010-08-29

ロリポップのサーバーに FTP で接続できない…

という事態に記事を書いている途中で陥り、けっこう焦ったのだけれど、
ロリポップ側でサーバー移転の作業をしていたよう。
あれ、そんなメール来てたっけ?? …ということで、ユーザー専用ページで「FTP サーバー」という項目を確認して、FTP ソフトでアクセスするホスト名の部分だけ書き換えると OK でした。

ちゃんとメール、確認しないと…。

2010-08-15

GAE/J、AuthSub で AuthenticationException …

アクセス解析にて「Japanese Bloggers Info 登録できない」という検索フレーズを発見、「えっ」と を確認してみると、確かにアプリケーションにブログを登録できず…。

問題は Blogger に所有ブログを問い合わせる際の AuthSub 認証で
com.google.gdata.util.AuthenticationException
が出ていたこと。

Google に登録している証明書の期限が切れたのかな? SSL は?とチェックしてみるも問題なし。うんうんうなること 10 数分…。最終的に、Google 先生に教えてもらいました。
String token = AuthSubUtil.getTokenFromReply(req.getQueryString());
token = URLDecoder.decode(token, "UTF-8");
URLデコードしてやれば良いようです。
これまでちゃんと動いていたんだけど、いつからデコードが要るようになったんだろう(汗)?アクセス解析をつけていて良かったとひしひしと感じた今日この頃。

検索してくれた人、ありがとう。プラス、この間ブログを登録できなかった人、すみません。

2010-08-12

PageRank の取得

GAE アプリに役立てられないかなと、ウェブページの Google ページランクを取得する Java コードを探していたところ、こんなのを発見。
public static String getHash(String url) {
    int c = 16909125;
    for (int d = 0; d < url.length(); d++) {
      String HASH_SEED_ = "Mining PageRank is AGAINST GOOGLE'S TERMS OF SERVICE. Yes, I'm talking to you, scammer.";
      c^ = ((int) HASH_SEED_.charAt(d%HASH_SEED_.length())) ^ ((int) url.charAt(d));
      c = c >>> 23 | c << 9;
    }
    return hexEncodeU32(c);
}
上記は、ページランク問い合わせの際に、ウェブページの URL とともに送るチェックサムの計算方法。ハッシュの種が
Mining PageRank is AGAINST GOOGLE'S TERMS OF SERVICE. Yes, I'm talking to you, scammer.
(ページランク掘り出しは Google の利用規約違反だってば!聞いてんの?この詐欺師!)
って…。使うなってことですね。はい。

2010-07-03

データベーススペシャリスト試験(DB)

午前Ⅰ:免除
午前Ⅱ:84.00
午後Ⅰ:63
午後Ⅱ:67

で、やっぱり何とか合格。何この午後の綱渡り…。

実は、今回は全く出来たという手ごたえがなくて、試験が終わった瞬間からすでに諦めモード。合格発表の確認や成績照会すらせずに、試験自体をすっかり忘れていたのだけれど、おととい急に証書が届いてビックリ。…こんなこともあるんだ。

一応使っていた本を紹介。


とにかく、2 つめの高度試験合格。で、次はやっぱりネットワーク!って、勉強の時間が取れればの話だけれど。

2010-06-13

GAE/J の AuthSub で This website has not registered with Google to establish a secure connection for authorization requests. の警告をなくす方法

GAE アプリから、 AuthSub 認証を使って Blogger などのデータにアクセスする際に出る警告がこちら。
This website has not registered with Google to establish a secure connection for authorization requests. We recommend that you continue the process only if you trust the following destination:
(このウェブサイトは認証リクエストに対して保護された接続を確立するよう Google に登録されていません。次のサイトが信頼できる場合のみ処理を続行してください:)
これを出なくさせたいなと思って調べてみたら、こちらがヒット。 そのままズバリなので、これ以上書く必要もないのだけれど…まあ一応。

ウェブサイトの登録

まず にアクセスして、「Add a New Domain」で自分の GAE アプリのドメインを入力する。「Manage registration」に「Manage YOURAPP.appspot.com」のように、入力したドメインが表示されるので、それをクリック。

Google Accounts Authentication API - Terms and Conditions を確認して、「I agree to the Terms of Service」ボタンを押下。ウェブサイトの管理ページが開くので、「Target URL path prefix」に認証後のトークン送信先 URL を入力し「Save」を押すと、Google へのウェブサイト登録が完了。

(場合によっては、ウェブサイトの「所有権の確認」が要る場合も。メタタグをサイトに追加するか、指示されたファイルをアップロードするかして、ウェブサイトの所有権を確認。)

再び AuthSub 認証を使ってみると警告文が変わっているはず。
This website is registered with Google to make authorization requests, but has not been configured to send requests securely. We recommend that you continue the process only if you trust the following destination:
X.509 自己署名証明書

「Google に登録されてはいるけれど、セキュアじゃないよ。」に警告が変わったので、次はセキュアトークンを送ってもらうための準備。 を参考に、X.509 の Self-signing Certificate を取得。Java の keytool が便利だった。
# Generate the RSA keys and certificate
keytool -genkey -v -alias Example -keystore example
  -keyalg RSA -sigalg SHA1withRSA
  -dname "CN=www.example.com, OU=Engineering, O=My_Company, L=Mountain  View, ST=CA, C=US"
  -storepass changeme -keypass changeme
keytool -export -rfc -keystore example -storepass changeme -alias Example -file mycert.pem
作成された PEM ファイルを、前節の Google アカウント情報「Upload new X.509 cert:」にアップロード。keystore は GAE の war に入れて、アップロード。しなきゃ認証時に java.io.FileNotFoundException が出ちゃう。

GAE 側の設定

Google Account Authentication API からセキュアトークンを受け取ったり、実際にデータをやりとりしたりする GAE のページを SSL に対応させる。

appengine-web.xml の appengine-web-app 要素内に
<ssl-enabled>true</ssl-enabled>
を追加。

お好みで web.xml に
<security-constraint>
 <web-resource-collection>
  <url-pattern>/authsub</url-pattern>
 </web-resource-collection>
 <user-data-constraint>
  <transport-guarantee>CONFIDENTIAL</transport-guarantee>
 </user-data-constraint>
</security-constraint>
をつけ加え(/authsub アクセス時は常に https になる)。

プログラム

Google Accounts Authentication API へのアクセスを要求する場合、
String next = "https://YOURAPP.appspot.com/authsub";
String scope = "http://www.blogger.com/feeds/"; //blogger にアクセスする場合
boolean secure = true;
boolean session = true;
String authSubLogin = AuthSubUtil.getRequestUrl(next, scope, secure, session);
とすると、authSubLogin にログイン用の URL が入るので、それでページにリンクを作成してアクセスしてもらう。

セキュアトークンを受け取って、実際にデータにアクセスするには、
String query = req.getQueryString();
String token = AuthSubUtil.getTokenFromReply(query);
PrivateKey privateKey = AuthSubUtil.getPrivateKeyFromKeystore("example", "changeme", "Example", "changeme");
String sessionToken = AuthSubUtil.exchangeForSessionToken(token, privateKey);

GoogleService myService = new GoogleService("blogger", "APPLICATIONNAME");//blogger にアクセスする場合
myService.setAuthSubToken(sessionToken, privateKey);
という風にするといいみたい(例外処理は省略)。後はもう好きなように
URL feedUrl = new URL("http://www.blogger.com/feeds/default/blogs");
Feed resultFeed = myService.getFeed(feedUrl, Feed.class);
とかすると、ユーザーが管理している Blogger ブログ一覧のフィードが取得できたりするわけ。

あ、セキュアトークン受け取りの URL を https に変えると、Google アカウント情報に指定する「Target URL path prefix」も方も https に変えとかないとね。

これで警告がすべてなくなるはずなんだけれど…、少し前にやったのを思い出しながら書いたので、思い違いや書き忘れがあるかも。後はより詳しい人のツッコミに期待。

2010-06-05

GAE でログのダウンロード

メールで流れてきた を読んで、ログがダウンロードできると今さらながらに知った次第。 にちゃんと書いてあるし…。が読んで見ても読解力のなさか、やはりうまくいかなかったので、分かりにくかった点を補足。

以下、Windows での話。

まず、appcfg.cmd はどこ???という状態。うちの場合ここにあった。
C:\eclipse\plugins\com.google.appengine.eclipse.sdkbundle.VERSION\appengine-java-sdk-VERSION\bin\appcfg.cmd

続いて
./appengine-java-sdk/bin/appcfg.sh request_logs myapp/war mylogs.txt
という風に使う…とのことだけれど、「myapp/war」って何を指定するの???と軽くパニック。結局何のことはない、ローカルの GAE プロジェクトの war フォルダへのパスを入れればよかったみたい。

appcfg.cmd のディレクトリを path に追加して、
appcfg request_logs C:\eclipse\workspace\MYPROJECT\war mylogs.txt
みたくしたらカレントディレクトリにちゃんと mylogs.txt が。よかったよかった。

あとは好みで --severity=0 とか、--append とかのオプションをつければいいわけね。フムフム。

2010-05-22

DeadlineExceededException と HardDeadlineExceededError

サーブレットの処理が 30 秒で終わらないときに出る
com.google.apphosting.api.DeadlineExceededException
というのは知ってたんだけれど、最近ログに現れた
com.google.apphosting.runtime.HardDeadlineExceededError
というのは一体何?…と思い調べてみた。

すると ajnk1 で話を聞いた bufferings さんのページを発見。 ほほう、30 秒ルールにひっかかって発生した DeadlineExceededException も、catch でつかまえたり、finally でロールバックしたりできるんだ。その処理に 300~400ms 近くかかってしまうと、次は HardDeadlineExceededError が出て有無を言わさず強制終了になってしまう…という理解でいいのかな。

300ms でできる処理って…。まあ何に使うのかわからないけど、一応覚えとこっと。 には、TaskQueue で呼ばれた Task で HardDeadlineExceededError が出た場合には、リトライがされないというような記述も。

としたら、DeadlineExceededException を catch とか、finally でロールバックとか、下手にしない方がいいのかも。

2010-05-09

GAE/J で Amazon Product Advertising API

今日は Google App Engine for Java で Amazon Product Advertising API を使うためのヒントみたいなもの。「Amazon Product Advertising API」は、2009 年 5 月まで「Amazon アソシエイト Web サービス」と呼ばれていたので、そちらの名前で覚えている人も多いかと。このサービスを使うとアプリケーションから簡単に Amazon の商品を検索することが可能に。この API 詳細は で理解してもらうとして、問題はその Signature ―― サービスの名称変更とともに API のリクエスト URL につけることが義務づけられた電子署名がちょっと大変かも。 リクエスト自体は、これまでのものにタイムスタンプを追加して、パラメータの値を RFC3986 のパーセントエンコード(URL エンコードとわずかに違う)、バイト順にパラメータを並べ替え。そのリクエスト全体の文字列をユーザーの秘密キーで HMAC(SHA256 アルゴリズム)を作成し、BASE64 に変換、パーセントエンコード後 Signature パラメータとしてリクエスト末尾につければ、ようやく API へのリクエストの URL の完成という流れ。ああ、ややこしい。

以前からこのサービスを使って、クリボウの Blogger 入門サイドバーとかで実際にキーワードを元に広告を表示しているのだけれど、これは何年か前に Perl で書いたのを を参考に、2009 年に書き直したもの。じゃあ GAE/J では、認証をどうするんだろう?と思ったので調べてみた。前置きが長かったけれど、結論は簡単。Amazon が公開している Java のサンプルコードを利用するだけ。 …だけ、とか書きながらこのままだと動かないのでちょっと修正。


サンプルコードの修正

import org.apache.commons.codec.binary.Base64;
まず、この BASE64 変換を行う org.apache.commons.codec.binary.Base64 のライブラリがないと思うので、以下からゲット。 Binaries 形式のものをダウンロード、解凍してできた commons-codec-*.*.jar をプロジェクトの WEB-INF/lib に入れてビルドパスに加える。
private String endpoint = "ecs.amazonaws.com"; // must be lowercase
private String awsAccessKeyId = "YOUR AWS ACCESS KEY";
private String awsSecretKey = "YOUR AWS SECRET KEY";
次に 33、34、35 の各行。34、35 行は、自身の Amazon Web Service アクセスキーと、 Amazon Web Service 秘密キーを入力。当たり前か。33 行は、そのままだとアメリカの amazon.com の結果を表示してしまうので、
private String endpoint = "ecs.amazonaws.jp"; // must be lowercase
と書き換えておく。

3つ目は、40 行からのコンストラクタで、UnsupportedEncodingException、NoSuchAlgorithmException、InvalidKeyException が出るぞと警告されてしまうので、throws するなり catch するなり。

最後は、なんでかわからないんだけれど、Signature に無駄な %0D%0A という文字列(パーセントエンコード前は「CRLF」改行)が入ってしまうのを阻止する。いや原因がわからず、阻止の仕方もわからないので、
signature = new String(encoder.encode(rawHmac));
らへんのコードを
signature = (new String(encoder.encode(rawHmac))).replaceFirst("[\\r\\n]*$", "");
と、パーセントエンコード寸前に最後の改行文字を削除させる。なんて対症療法!と思うけれど、まあこれで動くので。だれか詳しい人は教えて下さい。


使い方

HashMap<String, String> map = new HashMap<String, String>();
map.put("Service", "AWSECommerceService");
map.put("Version", "2009-01-06");
map.put("Operation", "ItemSearch");
map.put("SearchIndex", "Books");
map.put("ResponseGroup", "Small,Images");
map.put("Keywords", "Google App Engine");
map.put("AssociateTag", "kuribosblogge-22");
map.put("ItemPage", "1");

SignedRequestsHelper srh = new SignedRequestsHelper();
String url = srh.sign(map);
とかいう風に使うと、url にちゃんと AWSAccessKeyId や Timestamp が補われ、パーセントエンコード、ソートもされて、Signature の付加された URL が入るはず。やってみると…。
http://ecs.amazonaws.jp/onca/xml?AWSAccessKeyId=0E87C7QWYAM53DHJ2682&AssociateTag=kuribosblogge-22&ItemPage=1&Keywords=Google%20App%20Engine&Operation=ItemSearch&ResponseGroup=Small%2CImages&SearchIndex=Books&Service=AWSECommerceService&Timestamp=2010-05-09T08%3A30%3A56Z&Version=2009-01-06&Signature=VHsoRNX4slmxgG%2BS7xLKqcSf3Ogf9r7Qa1I8TILySMk%3D
ほらほら。これに URLFetch でアクセスすると、「Google App Engine」にヒットする書籍のデータ(画像も)が XML 形式で返ってくるので、解析してゴニョゴニョと上手く表示してやれば OK。ちなみに Signature の計算が間違っていると、
<?xml version="1.0"?>
<ItemSearchErrorResponse xmlns="http://ecs.amazonaws.com/doc/2009-01-06/"><Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message></Error><RequestID>17711f2a-00c6-415f-9995-6f3f96d0ae57</RequestID></ItemSearchErrorResponse>
みたいに返って来て、データは全く取れないので、チェックしてみるといいと思う。

This request caused a new process to be started for your application.

This request caused a new process to be started for your application, and thus caused your application code to be loaded for the first time. This request may thus take longer and use more CPU than a typical request for your application.
と Logs に出てくるのが気になっていたんだけれど…。

#appengine でスピンアップのリクエストの時ってWarningのログを出してくれている? "This request caused a new process to be started for your application"って出てるless than a minute ago via sobees


スピンアップした(アプリケーションが起動した)のがわかるようになってたのか。こりゃ便利。CPU 時間がいつもよりかかったリクエストの理由を知ったり、スピンアップ、スピンダウンの間隔を知ったりするのにいいね。

2010-05-01

Google App Engine Datastore Performance Test

Google App Engine Datastore Performance Test なるアプリを発見。

Google App Engine Datastore Performance Test

Python、Java の JDO、Java の Low-level API と、アクセスの手段別のデータストア処理時間が計測可能。

エンティティの追加に関しては Java の Low-level API を使った処理の方が Python 版より速いようだが、更新となると Python 版の方に軍配が上がるみたい。

…というか、JDO が遅すぎだということを再確認。

GAE に新しい Quota が加わるそうな

Tasks Strage Quota

Google App Engine のダッシュボードから Task Queues をクリックすると、見慣れない Quota 表示が。

Tasks Daily Quota として Task Queue API Calls があるのは前からのことなので気にならないのだけど、Tasks Storage Quota として、Task Queue Stored Task Count と Task Queue Stored Task Bytes という項目が新たに加わった様子。

画像は 4 月 29 日のもの。まだ適用されない Quota だとはいえ、171 % とか、1014 % とかいう数字は、刺激が強い…。同日行われた GAE のメンテナンスのせい…だと信じたい。

2010-04-17

Query.addSort(String).addSort(String) ではまる

Japanese Bloggers Info にカテゴリーページを追加。その後、カテゴリートップのページに、カテゴリ一覧を表示。

そこで気がついたのが、これ。カテゴリデータを保存しているエンティティを、親カテゴリを主キーに、日本語名を副キーに指定して並び替えるクエリ。
Query query = new Query("Category").addSort("japanese").addSort("parent");
みたいに、日本語名で並び替えてから、親カテゴリで並び替えればいいと思っていたのが大間違い。
Query query = new Query("Category").addSort("parent").addSort("japanese");
という風に
SELECT * FROM Category
ORDER BY parent, japanese
と SQL で書くのと同じ順で並べなきゃいけないみたい。

…。って API のリファレンスにも書いてあるじゃないですか。
addSort を初めて呼び出すと、並び替えの主キーとなるプロパティが登録されます。addSort を再び呼び出すと、並び替えの副キーなどが設定されます。
ううむ。ちゃんと読んどかないと…。

2010-04-11

Japanese Bloggers Info にカテゴリー機能追加

Japanese Bloggers Info にカテゴリー機能をつけてみた。

とりあえず、ベータ版としてこのブログを見ている人にだけお知らせ。 時間があるときに、トップページのようなプレビュー機能をつけるかもしれないけど、まだ未定。

Japanese Bloggers Info にブログを登録している人は、カテゴリー設定をしてみて、感想や不具合の報告などをコメントで書いてもらえるとうれしかったり…。

ちなみに、現在の設定では、カテゴリー登録順で上位に表示されるというウワサです。

2010-03-28

Blacklist

Google Appengine ダッシュボードに Blacklist の項目発見。いつついたんだろ?dos.xml でアプリケーションへのアクセス拒否ができるのか。フムフム。 いつもダッシュボードを見ているというわけにもいかないので、DoS 攻撃に即座に対応できるわけではないけれど、長時間にわたる攻撃は防げそう。覚えとこっと。

一瞬 dos-protection.xml とかが順当な名前のように思ったけれど、DoS 攻撃をしかけてくる IP アドレスを指定するファイルとなると…これでいいのかと思い直す。

2010-03-25

サムネイルのプリロード

Japanese Bloggers Info で、サムネイル画像をプリロードさせることに。最初のページ表示で少しもたつく気がするけれど、ブログのプレビューは画像読み取り時間がなくなり快適に。
var thumbs = new Array(blogurls.length);

function preload() {
  for (var i = 0; i < blogurls.length; i++) {
    thumbs[i] = new Image();
    thumbs[i].src = "http://capture.heartrails.com/medium?" + blogurls[i];
  }
}

if (window.addEventListener) {
  window.addEventListener("load", preload, false);
} else if (window.attachEvent) {
  window.attachEvent("onload", preload);
} else {
  window.onload = preload;
}

2010-02-28

Blogger ブログの ID ではまる

PubSubHubbub の使い方がなんとなく分かったので、Japanese Bloggers Info に導入しようと試行錯誤。意外と色々なところに手を入れなければいけないことに気づいたので、ついでにデータの保管の仕方も変えてしまう。

Blogger ブログはユーザーが自由にブログの URL を変えられるので、URL をキーにするより、ブログ ID をキーにした方がよさそうな気がして変更。当初あまり考えずにエンティティグループを作っていたのも、全部解除してバラバラに。

問題は、そのブログ ID。この値は、ユーザーのブログ一覧フィードから
tag:blogger.com,1999:user-875683517854.blog-13722714
のような形で取得できるのだけれど、実際のブログの投稿フィードの方から取得すると
tag:blogger.com,1999:blog-13722714
のようにユーザー情報が抜け落ちる。

ブログをアプリケーションに登録するときには、ブログ一覧をもとに上の方の ID をキーにしてエンティティを作るものの、PubSubHubbub の更新通知で送られてくるフィードに記載されているブログ ID は、下のユーザー情報が記載されていない方。

この違いに気づかず、ブログ情報のエンティティ読み出しで随分はまってしまった。ID の表記が複数あるとは思わなかったし。

結局、ブログ情報のエンティティのキーを登録時からユーザー情報抜きの形に加工するようにして、解決。

PubSubHubbub の更新通知のフィードには URL も記載されているので、前の URL をキーにエンティティを作っていたときの方が、連携が楽だった気もしたり…。

2010-02-20

GAE/J、SDK のアップデートではまる

Google App Engine Java SDK を 1.3.0 から 1.3.1 へアップデート。前のアップデートで少し手間取ったような記憶があるので、今回はメモを取りながら。

まずはインストール。eclipse の「ヘルプ > ソフトウェア更新」で開いた「ソフトウェア更新およびアドオン」ウィンドウの「使用可能なソフトウェア」から「Google Update Site for Eclipse *.*」を開き「Google App Engine Java SDK *.*.*」にチェック、「インストール」ボタンを押す。「インストール」という確認画面で「次へ」を押し、ライセンスのレビュー」でライセンスを確認後「使用条件の条項に同意します」ボタンを押下。インストール後 Eclipse の再起動を促す画面が出るので、「はい」ボタンを押す。

次は新しい SDK の選択。「ウィンドウ > 設定」から「Google」を開き「App Engine」をクリックすると、現在使っている SDK と新しくインストールした SDK が並んでいるので、新しいほうにチェックして「OK」ボタンを押す。これで SDK の更新はおしまい。

…のはずだったんだけれど。デプロイは問題がないものの、ウェブアプリケーションの実行ができなくなってしまった。あれれ。

コンソールには

Error occurred during initialization of VM
agent library failed to init: instrument
Error opening zip file: C:\eclipse\plugins\com.google.appengine.eclipse.sdkbundle_1.2.6.v200910130758\appengine-java-sdk-1.2.6\lib\agent\appengine-agent.jar

とのメッセージ。エージェントライブラリーの初期化に失敗するとのこと。…ってそんなディレクトリはもうとっくにないんですけど。

結局色々探して、eclipse のメニュー「実行 > 実行構成から GAE/J プロジェクトを選択、「引数」タブをクリックしてみると「VM 引数」の欄に問題のディレクトリ指定が。そこで

-javaagent:C:\eclipse\plugins\com.google.appengine.eclipse.sdkbundle.1.3.1_1.3.1.v201002101412\appengine-java-sdk-1.3.1\lib\agent\appengine-agent.jar

と、現在選択している SDK のエージェントのパスを指定してやると問題が解決。あーあせった。SDK 1.2.6 から 1.3.0 の更新のときは何も言われなかったのに、なんでだろう。

2010-02-19

GAE メンテナンス…

Google App Engine のメンテナンス。 から Reminder メールが来ていたので、知ってはいたものの未対処で、気づけば 1000 件近くものエラーが発生。TaskQueue で呼び出されたサーブレットがエラーを出して、Task が消えないうちに、さらに新たな Task がどんどん作られていったみたい。

メンテナンス中はデータストアが読み出し専用になって、データを格納しようとすると
com.google.apphosting.api.ApiProxy.CapabilityDisabledException
という例外が出るとのこと。

以下のページのように全部キャッチして、 TaskQueue を使うサーブレットは、メンテナンス時に書き込み動作をするような Task を積まないようにしないと…。
shin1ogawa: @bluerabbit777jp DS.put(new Entity("_capability_check", String.valueOf(Math.random()*10))); して読み込み専用例外をキャッチして判断するしか手が無いかもしれませんねぇ。 #appengineTwitter / shin1ogawa
でも、読み取り専用になっているかどうか、put() してみなければわからないというのは不便だなぁ。

2010-02-14

GAE/J で PubSubHubbub

今回は、Google App Engine for Java で PubSubHubbub を利用する話。

Japanese Bloggers Info、1 月に公開してから、現在の登録ブログ数が 250 近く。まだまだブログ数は伸びそうだけど、すでに CPU Time が 60% ぐらいになってしまっているので、なんとかしなくちゃと思っているところ。

ブログ更新の確認のために、cron と TaskQueue で、5 分ごとに全ブログのフィードにアクセスさせているのだけど、そこがやはり最も CPU を使っている部分。

ポーリングをやめていっそのこと PubSubHubbub にしてみたらどうだろう?と思って調べてみた。Blogger は Publisher として PubSubHubbub に対応しているので、こちらで Subscriber を用意すればいいというわけ。参考にしたのは、PubSubHubbub の仕様 と、実際の Hub。今ごろ気づいたけれど、これも GAE で動いてるんだ…。 この
http://pubsubhubbub.appspot.com/subscribe
という URL に、GAE アプリケーションから POST で Subscription Request を送る(パラメータはこちらを確認)と、hub.callback パラメータで送った Subscriber の更新通知用の URL に、折り返し確認の GET アクセスが来る。受け取ったアクセスのパラメータを確認(こちらを参考に)したら、そのまま hub.challenge の内容を本文にして、200 番台のステータスコードで返してやると、Hub への Subscribe 登録が成功。

最初の購読希望の POST アクセスは、Hub のデバッグページ(というか、ここが実際の Subscription Request の送り先…) から、フォームで送信できるので、色々テストしてみるのがよさそう。

購読登録が終了したフィードに更新があると、自動的にその更新部分のみを Atom フィードにして、POST で Subscriber の更新通知用の URL に送りつけてくれる。受け取ったという印に 200 番台のステータスコードを返せとのことだが、202 Accepted だと認識されず、5 回までリトライ、Failure のまま終了してしまった。200 OK を返してみると 1 回で認識。実際の Subscription 失敗の詳細は、前掲のデバッグページ「Subscriber Diagnostics」から確認できる。

更新情報の Atom フィードは POST の本文として送られてくるので、
Feed feed = new Feed();
try {
  feed.parseAtom(new ExtensionProfile(), req.getInputStream());
} catch (ParseException e) {
  e.printStackTrace(System.err);
}
という形で Feed にパースできる。後は
Entry entry = feed.getEntries().get(0);
String title = entry.getTitle().getPlainText();
String content = entry.getTextContent().getContent().getPlainText();
なり、なんなりと。

ということで、これはなかなか使えそうな予感。とはいえ、なかなかまとまった時間が作れないので、いつこれを組み込めることやら…ううむ。

2010-02-06

GAE/J、1000件超のエンティティ

Google App Engine for Java で、同じ Kind のエンティティは 1000 個までしか作れないのかな?と思ってドキュメントを探していたけど、データストアから一度に 1000 件までしか取得できない(Low Level API)というだけだったみたい…。cron で作りっぱなしだったあのエンティティは、今何個あるのかすらわからず…。

で、アクセスする方法を発見。 と実際のアクセスの仕方は分かったのだけれど、現在全く使用していないエンティティだったので、結局 1000 件ずつ取得→削除で、全部消しちゃった。1000 個で打ち止めと思い込んでいたんだけれど、知らないうちに 3000 個くらいたまってた…。無頓着にどんどんエンティティをためるシステムは考え直さないと。

2010-01-31

OPML と Google カスタム検索

Japanese Bloggers Info に、登録ブログを記載した OPML 発行と、登録ブログ対象のカスタム検索の機能をつけた。
  • ブログ情報から自動的に OPML ファイルを生成するサーブレットを作成。
  • Google のカスタム検索を作成し、OPML を登録、検索対象サイトを自動的に更新させる。
ということをした。OPML の仕様は、次のサイトを参考に。 カスタム検索への OPML 登録は、Google カスタム検索エンジン「マイ検索エンジン > コントロールパネル > 詳細設定」から。

「アノテーションフィードの追加」という項目に、Custom Search XML という、アノテーション(検索対象サイトを指定するもの)を記載した XML を登録する必要があるが、OPML をこの XML に変換するツールが用意されているので、それを間にかませる。つまり、

www.google.com/cse/tools/makeannotations?url=(OPML の URL)&label=(カスタム検索の ID)

のような URL を、アノテーションフィードとして登録すれば OK。検索フォームについては、検索結果をページ内に出すために、カスタム検索「マイ検索エンジン > Custom Search Element」を利用した。うん、なかなかいい感じ。

2010-01-21

GAE/J で If-Modified-Since に対応

フィードを配信するようになったので、ついでに If-Modified-Since ヘッダに対応させてみた。フィードリーダーからのアクセスがよくなるのかどうなのか、わからないのだけれど。

コードはほとんど、こちらの丸写し。変えたのは、Last-Modified をブログのリストをキャッシュしたときの日時にしたことぐらい。 なんとなく、フィードリーダーでのデータ更新の反映が早くなった気がする…。

GAE/J で RSS フィードの配信

でフィード配信開始しました。それがこちら。 フィードリーダーに登録しておくと楽しいかも。

RSS フィード発行のためのコードはこんな感じ。いつも載せるコードよりちょっと長いけれど、まあ備忘録として。
Feed feed = new Feed();
feed.setTitle(TextConstruct.create(TextConstruct.Type.TEXT, "Japanese Bloggers Info", null));
feed.setSubtitle(TextConstruct.create(TextConstruct.Type.HTML, "日本語の Blogger ブログの更新情報を紹介しています。", null));
feed.setGenerator("1.0", "http://japanese-bloggers.appspot.com/", "Japanese Bloggers Info");
feed.setUpdated(new DateTime(now.getTime(), 540));
feed.addLink("alternate", "text/html", "http://japanese-bloggers.appspot.com/");
feed.addLink("self", "application/rss+xml", "http://japanese-bloggers.appspot.com/feed");
feed.getAuthors().add(new Person("Kuribo", "http://www.kuribo.info/", "kurikuribo@gmail.com"));

ArrayList<Entry> entries = new ArrayList<Entry>();

Entry entry = new Entry();
entry.setTitle(TextConstruct.create(TextConstruct.Type.TEXT, blogtitle + ": " + posttitle, null));
entry.setContent(TextConstruct.create(TextConstruct.Type.HTML, summary, null));
entry.setPublished(DateTime.parseDateTime(postPublished));//postPublished は Blogger のフィードから得た発行日時
entry.addLink("alternate", "text/html", posturl);
entry.getAuthors().add(new Person(author));
entries.add(entry);

feed.setEntries(entries);

StringWriter sw = new StringWriter();
try {
 feed.generateRss(new XmlWriter(sw), new ExtensionProfile());
} catch (IOException e) {
 //省略
}
String rss = sw.getBuffer().toString();
ちょっとはまったのが、8 行目と 17 行目の author 項目。Feed.setAuthors(List<Person>) みたいなメソッドがないので焦ったのだけれど、上掲のように get した List(要素なし)に Person を追加するのでいいみたい。

あと 14 行目 content の項目は、アプリケーションでエスケープするので、text でいいやと思って指定してみるとフィードリーダーに本文が表示されず。タイトルは text、内容は html というのがよさそう。

2010-01-17

Quota と 10 秒ルールのはざまで

を公開してから一週間。現在、順調にユーザーが増えているところ。だけど、このままユーザーが増えても、無料の Quota 内でいけるのだろうかと少し心配に。 このアプリでは
  • Update サーブレットが cron で 5 分間隔に呼ばれ、登録されているブログ情報を確認して、TaskQueue で Check サーブレットを呼び出す。
  • Check サーブレットでは Update サーブレットから渡されたパラメータ(Key String)をもとにブログ情報を確認し、その投稿フィードへアクセス。更新があればその内容をブログ情報へ格納。
  • 登録ブログ分の Check サーブレットが全て動き終わると(MemcacheService.increment(Object, long) で確認)、Prepare サーブレットが起動。登録されているブログから更新時刻をもとに 50 個 Query を使って呼び出してリストを作成し、Memcache に格納。
  • トップページの Top サーブレットで、Memcache からリストを表示。なければ Prepare サーブレットのリスト作成メソッドから取得。
なんてことをしているんだけれど、今までは贅沢に、ブログの更新をチェックする Check サーブレットを、ブログの数だけ TaskQueue に積んで起動させていた。無料 Quota の Task Queue 呼び出しリミットは 100,000。このままの仕様だと、348 ブログ登録されただけで、一日の最後の方には「Over Quota」となってアプリが止まってしまう危険性が…。 そこで Check サーブレットの仕組みをいくらか変えて、複数ブログの更新チェックを一度にできるようにしてみた。とりあえず 10 件で。DatastoreService とか GoogleService とか、その他色んなリソースを使いまわせるので、パフォーマンス的にもいいかも。とか思って動かしてみると…

Request was aborted after waiting too long to attempt to service your request. Most likely, this indicates that you have reached your simultaneous dynamic request limit. This is almost always due to excessively high latency in your app. Please see http://code.google.com/appengine/docs/quotas.html for more details.

というログで、ションボリ。なんだこりゃと調べてみて、一番分かりやすかったのがこのページ。
 あのwarningは、リクエスト処理中に後続のリクエストが来てqueueにつまれ10秒以内に処理されなかったときに発生します。

 つまり、AppEgnineは30秒ルール以外にも、負荷が集中するときには、10秒以内に処理しないければいけないという10秒ルールも存在するのです。
ということなので、とりあえず 5 件ごとの更新チェックにしたところ、ようやく何も言われなくなった。登録ブログが次の上限 1736 件に迫ったら、また別の方法を考えないと…。

2010-01-11

GAE/J アプリ「Japanese Bloggers Info」完成

Google App Engine で作った初めてのアプリ、完成。日本語 Blogger ブログの更新情報を紹介するもの。 簡単な説明がこちら。 実際にアプリを公開して、色んな人に使ってもらって初めて気づくこともあったり。
  • まだ記事のないブログを登録する人がいる。
     →エントリにアクセスしないよう条件分岐。
  • 50 件も表示すると、最後の方のブログは右上のプレビューが見えない。
     →下半分は下半分用のプレビューエリアで表示。
  • ブログ名や投稿タイトルによっては、プレビューの JavaScript が止まってしまう。
     →JavaScript の配列に入れるメッセージのエスケープ。
  • ブログの Blogger 側の設定によっては、full フィードへのアクセス不能に。
     → default へのアクセスに切り替え。
  • Blogger の設定によっては、プロフィールへの URL が取れない。
     → プロフィール URL がなければプロフィールアイコン非表示に。
こういう新しいアプリは、不完全なままでもどんどん公開して、実際に使ってもらった方が、問題点が見えやすくなっていいんだなと実感。動かしながら修正して行くというのは、それはそれで大変なんだけれど。

2010-01-09

GAE/J、Blogger フィードのコンテントが取れない

Perl で昔に書いた Japanese Blogger Update Info の後継として、Blogger ブログの更新情報を掲載する GAE/J アプリを製作中。

Google アカウントと結び付けられた Blogger ブログを登録してもらって、トップページで時系列の更新情報を紹介。cron で一定時間(今のところ 5 分間隔)で Update というサーブレットが起動されるようにしていて、そこからまた TaskQueue で、各ブログのフィードへアクセスして更新を確認する Check サーブレットを複数起動する。全部チェックし終わったら更新されたブログのリスト(HTML)を作って、Memcache へ格納。トップページでそのキャッシュを表示、のようなことをしているのだけれど…。

ブログの投稿フィードのコンテント取得で一晩はまる。
GoogleService myService = new GoogleService("blogger", "kuribo-example-1");
Feed resultFeed = myService.getFeed("http://kuribo-programming.blogspot.com/feeds/posts/summary?max-results=1&redirect=false", Feed.class);//フィード取得。実際のフィード URL は Datastore から取得。
Entry entry = resultFeed.getEntries().get(0);//フィード記載の第 1 エントリ
String title = entry.getTitle().getPlainText();//タイトル
String url = entry.getLink("alternate", "text/html").getHref();//URL
String content = entry.getTextContent().getContent().getPlainText();//コンテント
のようにしていたところ、タイトルや URL は取れるが、コンテント(ブログ投稿の本文)が取れない。結局なんだったかというと、アクセスするフィード URL が悪かった。

Blogger のブログ投稿フィードは full(全文)、summary(冒頭)、default(ブログオーナーが設定している全文・冒頭のどちらか)の 3 種類でアクセスできるが、summary でアクセスした場合本文は <content type='html'> に入らずに <summary type='text'> に入るということが判明。そりゃ取れないや。

通信量を減らそうと summary にしていたのがアダとなったみたい。
http://kuribo-programming.blogspot.com/feeds/posts/full?max-results=1&redirect=false
にアクセスすれば、問題なしにコンテントが取れた。ううむ。まだまだ Blogger 研究が足りない。

追記(2010-01-11):

と思っていたんだけれど…、full ではエラーが出るフィードがあることに、アプリを公開してから気づく。

Blogger でフィードの公開設定を「完全」でなく「先頭のみ」にしていると、full では取れない…。「完全」の場合は、full でも summary でもフィードを取れるので、勘違いしていた。

…ということで、default でタイトルや URL などの情報をまず取得しておいて、コンテントが null だった場合にはまた別の処理(代替の方法が今思いつかないけど)を加えるのがよさそう。

追記(2010-01-11):

…って、
String summary = entry.getSummary().getPlainText();
で、サマリー取れるじゃないか、と今さら気づく。今までなんだったんだと思いつつ、おかげで知らなかった Blogger の Atom フィードの仕様が少し分かったので、まあよしとしよう。

2010-01-05

GAE/J のログ

あ、全然ログの取り方分かってなかった。
java.util.logging.Logger を使って、
log.info("なんとかかんとか");
とかしてるだけじゃだめなんだ。
今さらながら。

2010-01-02

GAE/J、GoogleService とか BloggerService とか

GoogleService myService = new GoogleService("blogger", "kuribo-example-1");
を使っていたのを、
BloggerService myService = new BloggerService("kuribo-example-1");
と書き換え、実行してみた途端にエラーが発生。

java.lang.NoClassDefFoundError: com/google/gdata/data/media/mediarss/MediaThumbnail

BloggerService を使うには前に書いたの に加えて、gdata-src.java-*.**.*/gdata/java/lib ディレクトリの
  • gdata-media-*.*.jar
も war/WEB-INF/lib に入れて、ビルドパスに通す必要があるみたい。

解決策は分かったものの、デプロイに 1 分近くかかるようになってしまったのと、単にフィードの基本的な項目を取得したかっただけというのもあって、結局 GoogleService に戻しちゃいました。