2010/11/20

JSON変換機能、slim3にマージ中

以前のエントリで書いたslim3モデルのJSON変換機能ですが、slim3のコミッターになってマージ中です!

ロードマップはこんな感じ。

1st step (完了).
・modelToJsonメソッドの追加。基礎的な仕組みの実装。
・short, int, long, float, double及びそれらのラッパークラスのJSON出力実装。
・StringのJSON出力実装(cipher=trueを考慮)。
・ラッパークラスとStringのList, Set, SortedSetのJSON出力実装。

2nd step (完了).
・AppEngine固有の型(Blob, Category, Email, GeoPt, IMHandle, Link,
PhoneNumber, PostalAddress, Rating, ShortBlob, Text, UserのJSON出力実装。
・Date, EnumのJSON出力実装。
・上記の型のList, Set, SortedSetのJSON出力実装。

3rd step以降.
・リファクタリング
・ModelRef対応。
・シリアライズ対応(おそらくシリアライズしてBase64)
・アノテーションによる制御の検討(フィールド名のエイリアスとか、JSON変換時は無視するとか)
・jsonToModelメソッド追加(と上記の型の対応)。

実装の途中経過は、適宜slim3のDiscussion Groupでお知らせしてます。

2010/11/09

slim3を改造して、モデルのJSON変換機能を追加する

Google App Engine for Java用のフレームワークと言えばslim3です。シンプルで非常に使いやすいフレームワークですが、その特徴はアノテーションプロセッサとして実装されている、Metaクラスジェネレータ(slim3-gen)にも当てはまります。拡張性が考慮されていて、簡単に機能を拡張することが出来るようになっていて、実際にアノテーションの情報を元にソースを生成するサンプルも公開されています(リポジトリの、slim3-gen-extention-sample)。

そのサンプルを参考に、slim3-genが生成するメタクラスにjson変換を行うメソッドを追加する拡張、slim3-gen-jsonicを作ってみたので、紹介します。

使い方

  1. slim3-gen-jsonicのバイナリを、ここからダウンロードして下さい(現在最新版はslim3-gen-jsonic-1.0.5.jar)。
  2. 次に、jsonicのバイナリをjsonicのサイトからダウンロードして下さい(現在最新版はjsonic-1.2.4.jar)。
slim3と使う場合と同様にプロジェクトを作成、セットアップし(slim3のスタートガイド参照)、jsonicのjarをビルドパスに追加、slim3-genのjar(現在最新はslim3-gen-1.0.5.jar)の代わりにslim3-gen-jsonicのjarを使用すると、モデルごとに生成されるメタクラスに以下のメソッドが追加されます。
  • public String modelToJson(Object model)
  • public ModelClass jsonToModel(String json)
ModelClassは、各モデルのクラスに置き換わります。

modelToJsonでモデルをJSON文字列に変換し、jsonToModelでその逆、JSON文字列をモデルに変換します。jsonToModelは、内部でJSONICを使っています。使い方は簡単、Metaクラスからこれらのメソッドを呼び出すだけです。

public void test(){
  TestModelMeta m = TestModelMeta.get();
  TestModel model = new TestModel();
  String json = m.modelToJson(model); // model -> JSON
  TestModel model2 = m.jsonToModel(json); // JSON -> model
}

対応している型

現在、以下の型に対応しています。
  • String(cipher=trueの場合、JSON出力時に暗号化されます), List<String>, Set<String>, SortedSet<String>
  • short, Short, List<Short>, Set<Short>, SortedSet<Short>
  • int, Integer, List<Integer>, Set<Integer>, SortedSet<Integer>
  • long, Long, List<Long>, Set<Long>, SortedSet<Long>
  • float, Float, List<Float>, Set<Float>, SortedSet<Float>
  • double, Double, List<Double>, Set<Double>, SortedSet<Double>

ソースからのビルド

EclipseとGoogle Plugin for Eclipse、Subversionクライアント(Subclipse or subversive)が必要です。

まず、slim3のリポジトリから、slim3とslim3-genをチェックアウトします。

次に、slim3-gen-jsonicのリポジトリから、slim3-gen-jsonic、slim3-gen-jsonic-testをチェックアウトします。

slim3-gen-jsonicに含まれているbuild.xmlを使ってビルドすると、slim3-gen-jsonicプロジェクトのtargetディレクトリに、jarファイルが生成されます(slim3-genの全クラスもこのjarに含まれています)。

slim3-gen-jsonic-testプロジェクトは、slim3プロジェクトを参照し、slim3-gen-jsonicプロジェクトが生成するjarをannotation processorとして参照しるので、このプロジェクト内にモデルクラスを作成すると、JSON変換機能を簡単に試せます。また、メタクラスはsrc_generatedディレクトリ内に生成されるようになっているので、実際にどんなメソッドが生成されているか確認できます。

jsonic単体と比べた利点

今のところ持っている機能だと、文字列の暗号化対応以外、jsonicそのものを使っても同じ効果が得られます。slim3-genに統合する形で機能を追加していく利点としては、
  • リフレクションを使わないので速い(但しある程度大きなモデルでも、数十ミリ秒の差でしょう)
  • App Engine固有の型に対応しやすい(固有の型には今後対応していく予定です)
というところが挙げられます。まぁ、あまり大きな利点は無いですし、DatastoreのモデルとJSONとしてクライアントに返すデータのモデルが同じものとも限らない(むしろ別々に設計したほうがよさそう)ですが、Metaを使ってモデルを手軽に変換できるだけでも便利なのではないかと思います。

2010/11/08

pos2wit - メールでTwitterを利用できる無料サービス



趣味でpos2witの開発を始めて、気づけばもう1年が経ちました。当初予定していたよりも多くの機能が実装できて、かなり便利に使えるツールになってきました。このスマートフォン全盛の時代、あえてガラケーのみで勝負する人のためのサービスです :-)

pos2witはメールでTwitterを利用出来るサービスです。特に携帯電話のメール機能に最適化してあり、携帯電話のメール機能を使って、Twitterの機能を一通り使うことが出来ます。写真の添付や他のユーザが投稿した写真のサムネイル取得にも対応しています。

ガラケーしか持っていない人、特にメール無料プランを契約している人に最適です(私もガラケーでメール無料プランです!)。通勤途中や休憩時間の暇つぶしなど、ちょっとした時間に是非ご活用ください!

注意事項: pos2witはGoogle App Engine上で動作しており、Twitterにアクセスします。そのため、App EngineのメンテナンスやTwitterのAPI制限に影響され、機能が正常に使えないことがあります。多くの機能は失敗することは殆どありませんが、検索については、TwitterのAPI制限のため、一定時間使えない状態になっていることがあります。そのため、検索結果の受信には、数分から数時間かかることがあります。

ユーザ登録


ユーザ登録は、PCからの登録(oAuth)と、メール経由の登録(xAuth)の2種類に対応しています。

PCから登録する場合、www.pos2wit.comにある、"Signin with Twitter"と書かれたボタンをクリックしてください。Twitterアカウントへのアクセスを許可する画面が表示されます。"許可する"を選ぶと、ユーザ登録が完了しますので、利用するメールアドレスを登録してください。登録後送られる確認メールに返信すると、メールが登録され、pos2witが利用出来るようになります。

メール経由で登録する場合、1行目にTwitterのIDを、2行目にパスワードを書いたメールを、reg@pos2wit.com に送信してください。ユーザ登録とメール登録が一度に行われ、pos2witが利用出来るようになります。(このとき送られたID, パスワードは、Twitterへのアクセス情報の取得にのみ使われ、取得後破棄されます。pos2wit登録後にパスワードを変更しても、動作に問題はありません。)

※迷惑メールフィルタを利用されている場合、pos2wit.comから送信されるメールを受信できるように設定しておいてください。


投稿機能


内容にTwitterに投稿したいツイートを記入して p@pos2wit.com にメールを送ると、Twitterに書き込むことが出来ます(件名は無視されます)。この際、写真が添付されていると、TwitPicに写真が投稿され、URLがツイートの末尾に付加されます。投稿先の画像サービスは、PCから pos2wit にログインした際に表示される、ダッシュボード画面で変更できます。

また、画像を投稿する際に、回転や反転などのフィルターを適用できます。件名にrlで左回転、rrで右回転を行ないます(このエントリ末尾に詳細)。


タイムライン取得


h@pos2wit.com に空メールを送ると、ホームタイムラインが取得できます。メールの受信可能サイズ制限のため、通常取得できるツイートは10〜20件程度です。sh@pos2wit.com に空メールを送ると、装飾のほとんどない、簡易フォーマットでホームタイムラインが取得できます。この場合、40〜50件程度のツイートが取得できます。

ユーザのタイムラインやステータス、お気に入り、リツイート、返信、ハッシュタグの検索、画像のサムネイル取得、イベント概要(ATND, TweetVite)取得、グルーポン概要取得など、ホームタイムラインの結果には様々な操作を行うためのメールアドレスへのリンクが貼られます。これはツイートの内容を解析することで実現されていて、短縮URLも展開して解析しています。


発表(App Engine Java Night Kansai #3)


先月京都リサーチパークで行われたAJNK3で発表しました! ustと資料はこちらからどうぞ(途中までだけど)
また、AJNK3では、slim3のひがやすをさん、teamsteamのpirosukeさんも発表されています。

各種機能の詳細

画像投稿先として対応しているサービス
  • twitpic
  • twitgoo
  • plixi
  • yfrog
  • img.ly
  • picasa
画像投稿時のフィルタ(件名に記入)
  • rl - 左回転(90℃)
  • rr - 右回転(90℃)
  • rp - 半回転(180℃)
  • fh - 上下反転
  • fv - 左右反転
  • ifh - 自動輝度調整
  • sNN - 縮小(例: s30 - 30%に縮小)
サムネイル取得に対応しているサービス
  • twitpic
  • twitgoo
  • plixi
  • yfrog
  • flickr
  • brizzly
  • owly
  • 携帯百景
  • mobypic

2010/07/08

Twitter4J 2.1.2を改造してGoogle AppEngineからTwitPicにOAuthEchoで画像を投稿する。

Twitter4J2.1.2では(執筆時はまだ開発中)、画像の投稿機能が提供されます。現在実装されているのは、YFrogへの投稿機能(OAuthEcho, Basic認証)とTwitPicへの投稿機能(Basic認証のみ)です。
pos2witではTwitPicへのOAuthEchoによる投稿を行いたかったので、できるように改造してみました。

まず、Twitter4Jのサイトから、バージョン2.1.2をダウンロードして展開して下さい。この中に含まれる、twitter-core内のソースを改造しますので、このソースが改造できる環境を整えて下さい(僕はEclipse上にプロジェクトを作成し、ソースをコピーしました。)。環境を整えてコンパイルすると、twitter4j.internal.logging内のいくつかのソースでコンパイルエラーが起こります。参照しているクラスがないためで、ここは何も考えずにCommonsLogging, Log4J, SLF4J関連のソースを削除します(それぞれLoggerとLoggerFactoryがあるのでそれらを削除)。

次にgithubのtwitter4j.utilから、ImageUpload.javaをダウンロードし、twitter4j.util内にコピーします。このファイル内で、画像投稿機能が実装されています。
コピーするとコンパイルエラーが出るので、まずはこれを潰します。

twitter4j.http.OAuthAuthorizationに、次のメソッドを追加します(これはgit内のソースには実装されています)。

public List<HttpParameter> generateOAuthSignatureHttpParams (String method, String url) {
long timestamp = System.currentTimeMillis() / 1000;
long nonce = timestamp + RAND.nextInt();

List<HttpParameter> oauthHeaderParams = new ArrayList<HttpParameter>(5);
oauthHeaderParams.add(new HttpParameter("oauth_consumer_key", consumerKey));
oauthHeaderParams.add(OAUTH_SIGNATURE_METHOD);
oauthHeaderParams.add(new HttpParameter("oauth_timestamp", timestamp));
oauthHeaderParams.add(new HttpParameter("oauth_nonce", nonce));
oauthHeaderParams.add(new HttpParameter("oauth_version", "1.0"));
if (null != oauthToken) {
oauthHeaderParams.add(new HttpParameter("oauth_token", oauthToken.getToken()));
}

List<HttpParameter> signatureBaseParams = new ArrayList<HttpParameter> (oauthHeaderParams.size());
signatureBaseParams.addAll(oauthHeaderParams);
parseGetParameters (url, signatureBaseParams);

StringBuffer base = new StringBuffer (method).append("&")
.append(encode(constructRequestURL(url))).append("&");
base.append(encode (normalizeRequestParameters(signatureBaseParams)));

String oauthBaseString = base.toString();
String signature = generateSignature (oauthBaseString, oauthToken);

oauthHeaderParams.add (new HttpParameter("oauth_signature", signature));

return oauthHeaderParams;
}


次に、ImageUpload.java内にTwitPic用のOAuth Echo対応ImageUploadを実装します。
同ファイル内に、下記のソースをコピーします。下記ソースは、同ファイル内のYFrogOAuthUploaderをコピーしてTwitPic用に修正したものです。


// import部分
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import twitter4j.conf.Configuration;
import twitter4j.conf.ConfigurationContext;
import twitter4j.internal.org.json.JSONException;
import twitter4j.internal.org.json.JSONObject;

// TwitPicOAuthUploader
public static class TwitPicOAuthUploader extends ImageUpload {
private String apiKey;
private OAuthAuthorization auth;

// uses the secure upload URL, not the one specified in the YFrog FAQ
private static final String TWITPIC_UPLOAD_URL = "http://api.twitpic.com/2/upload.json";
private static final String TWITTER_VERIFY_CREDENTIALS = "https://api.twitter.com/1/account/verify_credentials.json";

public TwitPicOAuthUploader(String apiKey, OAuthAuthorization auth) {
this.apiKey = apiKey;
this.auth = auth;
}

public String upload(File image) throws TwitterException{
throw new UnsupportedOperationException(); // AppEngineではFileInputStreamが使えない。
}

public String upload(String message, String fileName, InputStream fileBody) throws TwitterException {
// step 1 - generate verification URL
// String signedVerifyCredentialsURL = generateSignedVerifyCredentialsURL();

// step 2 - generate HTTP parameters
HttpParameter[] params = {
new HttpParameter("key", apiKey)
, new HttpParameter("message", message, true)
, new HttpParameter("media", fileName, fileBody)
};

// step 3 - upload the file
Configuration config = ConfigurationContext.getInstance();
config.getRequestHeaders().put("X-Auth-Service-Provider", TWITTER_VERIFY_CREDENTIALS);
StringBuilder b = new StringBuilder();
b.append("OAuth realm=\"http://api.twitter.com/\"");
for(HttpParameter p : auth.generateOAuthSignatureHttpParams("GET", TWITTER_VERIFY_CREDENTIALS)){
if(p.getName().equals("oauth_signature")){
try {
b.append(", ").append(p.getName()).append("=\"").append(
URLEncoder.encode(p.getValue(), "ISO8859_1")
).append("\"");
} catch (UnsupportedEncodingException e) {
throw new TwitterException(e);
}
} else{
b.append(", ").append(p.getName()).append("=\"").append(p.getValue()).append("\"");
}
}
config.getRequestHeaders().put("X-Verify-Credentials-Authorization", b.toString());
System.out.println("X-Auth-Service-Provider: " + TWITTER_VERIFY_CREDENTIALS);
System.out.println("X-Verify-Credentials-Authorization: " + b.toString());
HttpClientWrapper client = new HttpClientWrapper(config);
HttpResponse httpResponse = client.post(TWITPIC_UPLOAD_URL, params);

// step 4 - check the response
int statusCode = httpResponse.getStatusCode();
if (statusCode != 200) {
throw new TwitterException("Twitpic image upload returned invalid status code", httpResponse);
}

try{
String response = httpResponse.asString();
return new JSONObject(response).getString("url");
} catch(JSONException e){
throw new TwitterException("Unknown Twitpic response", httpResponse);
}
}
}


上記のソースをペーストすると、twitter4j.internal.http.HttpParameterに必要なメソッドが無いためにコンパイルエラーが発生します。下記のフィールド及びメソッドを、HttpParameterクラスに追加して下さい。


// フィールドを追加
private boolean noUrlEncode = false;
private InputStream fileBody = null;
...

// コンストラクタを追加
public HttpParameter(String name, String value, boolean noUrlEncode) {
this.name = name;
this.value = value;
this.noUrlEncode = noUrlEncode;
}

public HttpParameter(String name, String fileName, InputStream fileBody) {
this.name = name;
this.file = new File(fileName);
this.fileBody = fileBody;
}
...

// メソッドを追加
public boolean isUrlEncodeUnnecessary(){
return this.urlEncodeUnnecessary;
}

public boolean hasFileBody(){
return null != fileBody;
}

public InputStream getFileBody(){
return fileBody;
}


これでコンパイルエラー自体は消えますが、追加されたパラメータに対応してリクエスト作成機能を修正する必要があります。リクエスト作成機能はtwitter4j.internal.http.HttpClientImpl.java内にあるので、下記のように改造します(赤字が改造部分)。

                                // 241行目あたり
if(param.isFile()){
write(out, boundary + "\r\n");
write(out, "Content-Disposition: form-data; name=\"" + param.getName() + "\"; filename=\"" + param.getFile().getName() + "\"\r\n");
write(out, "Content-Type: " + param.getContentType() + "\r\n\r\n");
BufferedInputStream in = new BufferedInputStream(
param.hasFileBody() ? param.getFileBody() : new FileInputStream(param.getFile())
);
int buff = 0;
...

// 258行目あたり
logger.debug(param.getValue());
if(param.isUrlEncodeUnnecessary()){
out.write(param.getValue().getBytes("UTF-8"));
} else{
out.write(encode(param.getValue()).getBytes("UTF-8"));
}

write(out, "\r\n");
...


この修正が必要なのは、
  • オリジナルのコードではFileInputStreamを使っているが、これはAppEngineでは使えない。
  • オリジナルのコードでは常にパラメータをURLEncodeしているが、メッセージをURLEncodeするとTwitPicで復元されない(URLEncodeされた状態で記録される)

という問題に対処するためです。

これでTwitPicに画像を投稿できるようになります。サンプルコードはこんな感じ。投稿にはTwitPicのAPIKEYが必要なので、ここからアプリケーションを登録し、APIKEYを入手して下さい。
 public void test() throws Exception{
String apiKey = TwitPicのAPI KEY;
Twitter t = new TwitterFactory().getOAuthAuthorizedInstance(new AccessToken(
Twitterユーザのaccess_token
, Twitterユーザのaccess_token_secret
));
OAuthAuthorization auth = (OAuthAuthorization)t.getAuthorization();
String message = "投稿するメッセージ。";
String url = new ImageUpload.TwitpicOAuthUploader(apiKey, auth).upload(
message, "duke.jpg", getClass().getResourceAsStream("duke-on-gae.jpg")
);
// twitterには、別途投稿する必要がある(uploadでは写真がTwitPicに投稿されるのみ)。
t.updateStatus(message + " " + url);
}


だいぶ汚い改造になってしまいましたが、とりあえず投稿することはできるようになりました。もう少し整理して、Twitter4Jにうまく取り込んでもらえるよう働きかける予定です。

AJDT2.1.0 Released

AJDT2.1.0がリリースされました。リリースノートに書かれている変更点をざくっと紹介します。主にITD(Inter Type Definition)関連の機能が強化されました。

  • ITD-Aware Search
    アスペクトによる定義の追加を考慮したJava Searchが正しく行われるようになりました(例: ITD定義、String Person.getName()に対して参照の検索を行うと、ITDされたメソッドを使用しているコードがヒットする)。

  • ITD rename refactoring
    名前変更のリファクタリング機能がITDに対応しました。アスペクトのITDでの名称変更(例: String Person.getName()をString Person.getFirstName()に)を行うと、影響を受ける定義(例: Personの派生クラスでのgetNameメソッド(オーバーライド))も名称変更されます。

  • ITD-aware renaming of getters and setters
    フィールドの名称変更を行う際に、ITDによって実装されていたそのフィールドのgetter及びsetterの名称変更も行えるようになりました。nameをfirstNameに変更した場合に、ITDで実装されたgetNameがgetFirstNameに、setNameがsetFirstNameに変更されませす。(但し、Personの派生クラスでgetNameをオーバーライドしていた場合、そのメソッドは変更されません(あくまでgetter/setterのみ)。個人的にはこれも変更されて欲しいところ。常に@Override付けといた方が良さそうですね。)

  • Pull-out refactoring
    フィールドやメソッドを、ITDとして特定のアスペクトに移動するリファクタリング。パッケージエクスプローラでフィールドやメソッドを選んで右クリック -> Refactory -> Pull Out ITD... と選択すると、既存のアスペクトに切り出せます。

  • Aspect-aware type renaming
    クラス名の変更を行う際に、それを参照しているアスペクト内での定義(ITD)も適切に変更されるようになりました。

  • @Test and Intertype declarations
    ITDによって@Testが付加されたメソッドを追加した場合、そのクラスをテストすると、@Testが付加されたメソッドもテストの対象になります。

  • AspectJ-aware PDE Build (Helios only)
    AspectJをPDEに統合したビルドが提供されるようになったそうです。AspectJに依存したプラグインを作る際に、Javaプラグインと同じウィザード等を使えるようになりました。詳しくはこちら

  • Modernize the build server
    ビルドサーバーをモダンなものに変えたそうです。詳しくはこちら

  • AspectJ 1.6.9
    最新のAspectJ 1.6.9がバンドルされています。readmeはこちら

  • Bug Fixes
    その他、いくつかのバグが修正されています。


最初ITDがサポートされるようになった頃は、IDE上はコンパイルエラーになっていましたが、バージョンを重ねるにつれて洗練され、遂にリファクタリングまで正常に動くようになりました。今回の機能強化で、ITDされたメソッド・フィールドも元からクラスに定義されていたものとほぼ同じように扱われるようになり、心おきなく使えるようになったのではないでしょうか。

2010/06/19

wicketのoauth認証サンプルをappengineに載せる

6/20追記 index.htmlの削除と、エラー処理の修正。


矢野 勉さんの記事、Wicketで始めるオブジェクト指向ウェブ開発第9回 WicketによるOAuth認証を、Google App Engineに載せてみました。appengine上のURLはこちら

ソースコードの修正は、appengineに載せるための修正と、Twitter4Jの最新版を使うための修正です。

1. ソースコードの入手


第9回のソースコードをダウンロードし、解凍する。

2. AppEngineプロジェクトの作成


Eclipseで新しいAppEngineプロジェクトを作成。プラグインの入手方法やプロジェクトの作成方法はこちら(山下さんの記事)。作成する際に下記の設定を行う。
  • プロジェクト名はwicket-sample-oauth
  • パッケージは"jp"
  • User Google Web Toolkitはオフ

作成後、下記のファイルを削除。(6/20 追記)

  • war/index.html


3. ライブラリの入手


下記のライブラリをダウンロードし、war/WEB-INF/lib内に必要なjarを置いて、ビルドパスに追加。

  • Apache Wicket
    ・wicket-1.4.9.jar
  • SLF4J
    ・slf4j-api-1.6.0.jar
    ・slf4j-jdk14-1.6.0.jar(又はお好みでLog4J用やcommons-logging用を)
  • Twitter4J
    ・twitter4j-core-2.1.2.jar


4. ソースコード上書き


解答したソースを、下記のように上書き。

  • src/main/javaをプロジェクトのsrcに。
  • src/webapp/WEB-INF/web.xmlをプロジェクトのwar/WEB-INFに。


5. ソースコード修正


上書きしたソースの、下記の部分を修正(が変更点)。

  • jp.gihyo.wicket.TwitterClientを削除。このクラスは、当時のTwitter4Jがシリアライズに対応していないために用意されたクラス。最新版ではシリアライズに対応しているため、不要です。
  • jp.gihyo.wicket.AppSession#login

    下記は、Twitter4Jの最新版を使うための修正です。
        public boolean login(String pin, Response response) {
    try {
    if(requestToken == null) {
    throw new IllegalStateException("requestToken is missing.");
    }

    Twitter client = new TwitterFactory().getInstance();
    client.setOAuthConsumer(consumerKey, consumerSecret);

    AccessToken accessToken = client.getOAuthAccessToken(requestToken, pin);
    client = new TwitterFactory().getOAuthAuthorizedInstance(
    consumerKey, consumerSecret, accessToken);

    this.twitterSession = client;
    dirty();
    return true;
    } catch(TwitterException ex) {
    LOGGER.error("Can not setup OAuth Access Token to Twitter object.", ex);
    return false;
    }
    }


  • jp.gihyo.wicket.AppSession#getTwitterSession

    下記は、Twitter4Jの最新版を使うための修正です。
        public Twitter getTwitterSession(Request request) {
    if(request == null) throw new IllegalArgumentException("'request' is missing");

    //既にTwitterオブジェクトを作成済みなら,認証の必要はない。
    if(twitterSession != null) return twitterSession;

    //AccessTokenをDBなどに永続化している場合は,ここで永続化したAccessTokenを使って
    //Twitterオブジェクトを再作成して返却すればよい。
    //このサンプルではAccessTokenを永続化していないので,すぐにOAuth認証に入る。

    //OAuth認証開始。RequestTokenの取得を試みる。
    Twitter client = new TwitterFactory().getInstance();
    client.setOAuthConsumer(consumerKey, consumerSecret);
    ...


  • jp.gihyo.wicket.WicketApplication

    以下のメソッドを追加。Wicketアプリケーションをappengineに載せるための設定です。何故これが必要かはここに解説があります。
     @Override
    protected ISessionStore newSessionStore() {
    return new HttpSessionStore(this);
    }

    @Override
    public String getConfigurationType() {
    return Application.DEPLOYMENT;
    }


  • jp.gihyo.wicket.AppSession#getTwittersession(6/20 追記)

    以下の修正を行う。Twitter呼出時の例外を修正。Twitterがエラーを返した場合の処理を改善します。

    public Twitter getTwitterSession(Request request) throws TwitterException{
    if(request == null) throw new IllegalArgumentException("'request' is missing");

    //既にTwitterオブジェクトを作成済みなら,認証の必要はない。
    if(twitterSession != null) return twitterSession;

    ...

    RequestToken token = null;
    try {
    token = client.getOAuthRequestToken(RequestUtils.toAbsolutePath("login"));
    } catch(TwitterException ex) {
    throw ex;
    }
    ...



6. Twitterにアプリケーションを登録


アプリページへ行き、アプリケーションを登録する。
登録方法は、第9回 WicketによるOAuth認証の記事内にあります。

登録後、consumer-keyとconsumer-secretをweb.xml内に設定。

7. セッションを有効にする


war/WEB-INF/appengine-web.xml内に、次の1行を追加する。

<sessions-enabled>true</sessions-enabled>




以上の設定・変更を行った上で配備すると、appengineでサンプルが動作するようになります。
動作例はこちら