*/ -->
ラベル GAE/J の投稿を表示しています。 すべての投稿を表示

JavaサーブレットでThreadLocal<T>を使う時のメモ。実行環境は、GoogleAppEngineです。

そもそも使って大丈夫?

tomcatやGoogleAppEngineでは、実行される際にプールされたスレッドに対して各リクエストを割り当てます。つまり、ThreadLocal<T>を利用しても、各リクエストに対して新しいスレッドが生成されるわけではないので、場合によっては古い内容をThreadLocal<T>が返す可能性があります。

しかし、考え方を変えると「各リクエストは割り当てられたスレッドで完結する」ということなので、新しいリクエストが発生し、サーブレットが起動した時にThreadLocal<T>に値をセットしなおすことで使用しても問題なさそうです。

ただし、スレッドをまたぐような処理を行うと途端にバグとなるので注意が必要です。

そもそもなぜこの疑問が沸いたのかというとSlim3のRequest(Response)Locatorの実装を呼んでいて「これってスレッドセーフなの?」と思ったからです。冷静に考えれば一発で分かることなのに恥ずかしい。

結論

Servletの起動毎にちゃんと初期化(再セット)してあげれば大丈夫。

link

この記事の続きを読む

GoogleAppEngineのCookieに関する備忘録です。

java.net.HttpCookie

  • HttpCookie+toStringは、HttpCookie+setMaxAgeしてあっても、HttpCookie+toStringで吐く文字列には”Max-Age”を印字しない。
    • 逆に吐くのは、 name-value, path, domain, portだけ。
    • HttpCookie+toStringを直接”Set-Cookie”ヘッダに利用するときは注意

javax.servlet.http.Cookie

  • 既にセットされたCookieをサーブレットから取得した場合、path値に”/”を指定したにもかかわらず、なぜかnullになる。
    • 自分のミス or GoogleAppEngine固有の問題?

javax.servlet.http.HttpServletResponse

  • HttpServletResponse+addCookieがなぜか動作しない。
    • 自分のミス or GoogleAppEngine固有の問題? もしかしてsession-enableがoffだと使えないとか?
    • HttpServletResponse+setHeaderでSet-Cookieしてあげると正常に動作。
    • GoogleAppEngineでCookieを利用する
link

この記事の続きを読む

普通にやる場合は、HttpServletResponse+addCookieで新しいクッキーを登録してあげればうまく動作するはずなんだけど、何度やってもクッキーが保存されない!。OAuth認証できないじゃん。イライラ。

頭にきたので自分でHttpServletResponse+setHeader直叩き。すると普通に動いた。イライラ。GMT形式のタイムスタンプを使わないとExpiresが設定できないので適当にSimpleCookieクラスを作った。時間の無駄。イライラ。以下ソースコード。

link

この記事の続きを読む

たとえばテンプレート側でDate型の日付をSimpleDateFormat#formatしたいなーとかURLEncoder#encodeしたいときは普通にVelocityContextに突っ込んであげればいいみたい。

Java

VelocityContext context = ...;
context.put("URLEncoder", URLEncoder.class);
context.put("DateFormatter", new SimpleDateFormat(...));
context.put("Text", "ああああああ");
context.put("Date", new Date());
Template template = ...;
template.merge(context, response.getWriter());

Template

${URLEncoder.encode(${Text},"UTF-8")}
${DateFormatter.format(${Date})}

まさかこんな芸当が出来るなんて思ってなくてびっくり(特にURLEncoderの方)。でもこれってパフォーマンス的にどうなんだろ・・・と、思いつつ便利なので使ってしまう。

関連記事:
[GAE/J] Velocityを利用した時の文字化け対処メモ
link

この記事の続きを読む

前からちょっとずつ組んでいたGoogleAppEngineを使った掲示板が形になるまで時間がかかったけどなんとか動くところまで進んだ。理由としては、無駄にMemcacheを使ったキャッシングとか、ブラウザキャッシュとかの機能を追加してたら時間がかかった・・・(そんなアクセス数稼げるわけないのにねw)。とりあえずどの程度負荷がかかるのかとか、どの程度で課金対象になるのかが知りたかったのでデプロイしてみた。

MokuMoku!(仮)β - http://moku2w.appspot.com/

荒らしは勘弁だけど、反応は大歓迎です。宜しくお願いします。

主な仕様

  • 書き込み/新規ロール作成にはGoogleアカウントが必要
  • 2日書き込みが無かった場合2ちゃんねるでいうところのDAT落ちというか消滅
  • スレッドをロールって言っているのは、1000レスを超えると1レスからスタートする様から命名。一つ前のレスのみ参照可能。それ以外は削除というか消滅。
  • リンク(http, https, ftp)が本文に指定された場合オートリンク。画像の場合はリンクして、本文の末端にサムネイルを追加。
  • 改行は確か30回まで。
  • 書き込んだのに更新されない場合は「Ctrl+F5」でキャッシュを無視して再読み込みするといいかも。
  • GoogleChrome or FireFox推奨。IEとかだと激しく外見が汚い。
関連記事:
クラウド掲示板「MokuMoku!」アップデート
link

この記事の続きを読む

データストアに対して部分一致(SQLのLIKEみたいな)検索をかける方法はないかと探してみたところSlim3 DatastoreのDatastore.filterInMemory()を使って、条件をmeta.{value}.contains(String)でやるのが唯一の手段っぽい。けどこの方法はデータストアに高い負荷がかかるので、検索フォームなどで利用する以外は、使用しないほうがいいと思う。

String search = "test";
EntityMeta meta = new EntityMeta();
List<Entity> list = Datastore.query(meta).filterInMemory(meta.title.contains(search)).asList();
link

この記事の続きを読む

GAEのコンソール画面の日付のタイムゾーンってどこのタイムゾーンが適応されているのかな?と思ったので調べてみるとどうやらPST:太平洋標準時間らしい。

これをどうにかして日本時間に出来ないかなと思ってLog4jを使ってみたけどコンソール自体のタイムスタンプは変わらないみたいだし、”[appname/*.***************].<stdout>:”みたいなのがくっついてかっこ悪いし、見難い(もしかしたらやり方が間違ってる?)。

そこでjava.util.LoggerのFormatterをSimpleFormatterから独自のものにするため、/war/WEB-INF/logging.propertiesを修正してみる。

link

この記事の続きを読む

自分でコントローラを作っていると、PathInfoから適応するクラスを選択する時にリフレクションを利用するか、if文連発で制御するしか頭になかった。けどやっぱり面倒なのでJavaあんまり分からないなりに、変換テーブル作って自動で対応するクラスをロードする機能を作ってみた。

目標としては、以下の3つ。

  1. 新しいページの追加が楽。
  2. リフレクションを使わない。
  3. 無駄にクラスをnewしない。
link

この記事の続きを読む

データストアにエンティティを格納する際に以下のようなエラーが出た。

Class *** has multiple relationship fields of type ***

察するにデータストアに格納されるエンティティが保持できる同一の型の子エンティティが2つ以上あると吐くエラーみたい。少し古い記事だけど↓でも議論されてるみたい。

Google-App-Engine-Japan : 同一型の子Entityを持つと、子Entityの判断がつかない

実際以下のようなコードだとエラー吐く。

@Element(dependent = "true")
private List<TestEntity> newEntity = new ArrayList<TestEntity>(); @Element(dependent = "true")
private List<TestEntity> oldEntity = new ArrayList<TestEntity>();

上の例だとTestEntityを継承した空クラスを指定してもやっぱりエラー。

対処法は設定ファイルを書き換えるか、継承を使わないで中身が全く同じクラスをコピペ作成するか、っぽい。

jdoconfig.xml
<property name="datanucleus.appengine.multipleRelationsOfSameTypeAreErrors" value="false"/>

なんか設定ファイル弄って無理やり動かすのも癪なのでクラスを継承しないで作って解決。

link

この記事の続きを読む

Memcacheにjava.util.List型のオブジェクトをキャッシュしようとすると「java.lang.IllegalArgumentException: Cannot use as value:…」という例外を吐く場合の対処法メモ。

とりあえず下記のコードでなんとかエラー吐かなくなった。似非プログラマーだから理由が分からない。詳しい方、代案や理由など教えていただければ幸いです。

List<String, String> list = new ArrayList<String, String>();
list.put("key", "test");

Cache cache = null;

try
{
CacheFactory factory = CacheManager.getInstance().getCacheFactory();
cache = factory.createCache(Collections.emptyMap());
}
catch(CacheException ex)
{
	return;
}

try
{
	cache.put("cache", new ArrayList<String, String>(list));
}
catch(GCacheException ex){}

新しいオブジェクト作ってコンストラクタに古いリストを指定したら直った。適当すぎる。

2011/9/30 追記

コード自体が支離滅裂なのでまったく当てにならないエントリです。申し訳ありません。

link

この記事の続きを読む