2009年7月30日木曜日

よぅやく eclipse #galileo (3.5) に対応した #appengine plugin がリリースされた

まだアナウンスはされていないようですが、リリースされています。自分はeclipse-SDK-3.5-macosx-cocoa-x86_64で確認しました。アップデートサイトのこれまで同様の命名規則のURLで、以下のとおり。

dropinのフォルダを作りたい人向けの情報

features
  • features/com.google.appengine.eclipse.sdkbundle.e35.feature_1.2.2.v200907291526/
  • features/com.google.gdt.eclipse.suite.e35.feature_1.1.0.v200907291526/
  • features/com.google.gwt.eclipse.sdkbundle.e35.feature_1.7.0.v200907291526/
plugins
  • plugins/com.google.appengine.eclipse.core_1.1.0.v200907291526.jar
  • plugins/com.google.appengine.eclipse.sdkbundle_1.2.2.v200907291526/
  • plugins/com.google.gdt.eclipse.core_1.1.0.v200907291526.jar
  • plugins/com.google.gdt.eclipse.platform.e35_1.1.0.v200907291526.jar
  • plugins/com.google.gdt.eclipse.platform.shared_1.1.0.v200907291526.jar
  • plugins/com.google.gdt.eclipse.platform_1.1.0.v200907291526.jar
  • plugins/com.google.gdt.eclipse.suite_1.1.0.v200907291526.jar
  • plugins/com.google.gwt.eclipse.core_1.1.0.v200907291526.jar
  • plugins/com.google.gwt.eclipse.sdkbundle.macosx_1.7.0.v200907291526/
  • plugins/javax.xml_1.3.4.v200902170245.jar
  • plugins/org.apache.xerces_2.9.0.v200909240008.jar
  • plugins/org.apache.xml.resolver_1.2.0.v200902170519.jar
  • plugins/org.apache.xml.serializer_2.7.1.v200902170519.jar
  • plugins/org.eclipse.emf.common_2.5.0.v200906151043.jar
  • plugins/org.eclipse.emf.ecore.change_2.5.0.v200906151043.jar
  • plugins/org.eclipse.emf.ecore.edit_2.5.0.v200906151043.jar
  • plugins/org.eclipse.emf.ecore.xmi_2.5.0.v200906151043.jar
  • plugins/org.eclipse.emf.ecore_2.5.0.v200906151043.jar
  • plugins/org.eclipse.emf.edit_2.5.0.v200906151043.jar
  • plugins/org.eclipse.jem.util_2.0.200.v200905140200.jar
  • plugins/org.eclipse.wst.common.core_1.1.201.v200806010600.jar
  • plugins/org.eclipse.wst.common.emf_1.1.300.v200905060430.jar
  • plugins/org.eclipse.wst.common.emfworkbench.integration_1.1.300.v200811200330.jar
  • plugins/org.eclipse.wst.common.environment_1.0.202.v200807161459.jar
  • plugins/org.eclipse.wst.common.frameworks_1.1.300.v200904160730.jar
  • plugins/org.eclipse.wst.common.project.facet.core_1.4.0.v200906080535.jar
  • plugins/org.eclipse.wst.common.uriresolver_1.1.301.v200805140415.jar
  • plugins/org.eclipse.wst.sse.core_1.1.400.v200905141750.jar
  • plugins/org.eclipse.wst.sse.ui_1.1.100.v200905141750.jar
  • plugins/org.eclipse.wst.validation_1.2.102.v200905201610.jar
  • plugins/org.eclipse.wst.xml.core_1.1.400.v200904300540.jar

#appengine で ArrayStoreException

昨晩、Google App Engineにデプロイした環境でjavax.servlet.ServletException:... Caused by: java.lang.ArrayStoreException: [Ljava.lang.String;とかいうExceptionがずーっと出ていて、困ってた(詳細はこのエントリの最後に貼付けてある)。

Wicket を使っているし、頻繁にアプリケーションをデプロイし直していた途中だったので、セッション周りだろーなー、とか色々考えてたり。

で、今日はその問題の解決を!と思っていたワケですが、現象が出なくなっているんだな…。何もしてないのに。誰か同じ現象に出くわした方はおられませんか?そして、何か具体的に対処を施して解決したなら、教えてもらえませんか?ML に投げたいところなんだけど、如何せん現象がもう出ないので投げづらいw

ちなみに、ぐぐったら以下のふたつがヒントになりそーなんだけども…。

問題のException

javax.servlet.ServletException: java.lang.ArrayStoreException: [Ljava.lang.String;
    at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:239)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
    at org.mortbay.jetty.Server.handle(Server.java:313)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:506)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:830)
    at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:381)
    at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:139)
    at com.google.apphosting.runtime.JavaRuntime.handleRequest(JavaRuntime.java:235)
    at com.google.apphosting.base.RuntimePb$EvaluationRuntime$6.handleBlockingRequest(RuntimePb.java:4823)
    at com.google.apphosting.base.RuntimePb$EvaluationRuntime$6.handleBlockingRequest(RuntimePb.java:4821)
    at com.google.net.rpc.impl.BlockingApplicationHandler.handleRequest(BlockingApplicationHandler.java:24)
    at com.google.net.rpc.impl.RpcUtil.runRpcInApplication(RpcUtil.java:359)
    at com.google.net.rpc.impl.Server$2.run(Server.java:820)
    at com.google.tracing.LocalTraceSpanRunnable.run(LocalTraceSpanRunnable.java:56)
    at com.google.tracing.LocalTraceSpanBuilder.internalContinueSpan(LocalTraceSpanBuilder.java:516)
    at com.google.net.rpc.impl.Server.startRpc(Server.java:775)
    at com.google.net.rpc.impl.Server.processRequest(Server.java:348)
    at com.google.net.rpc.impl.ServerConnection.messageReceived(ServerConnection.java:436)
    at com.google.net.rpc.impl.RpcConnection.parseMessages(RpcConnection.java:319)
    at com.google.net.rpc.impl.RpcConnection.dataReceived(RpcConnection.java:290)
    at com.google.net.async.Connection.handleReadEvent(Connection.java:428)
    at com.google.net.async.EventDispatcher.processNetworkEvents(EventDispatcher.java:762)
    at com.google.net.async.EventDispatcher.internalLoop(EventDispatcher.java:207)
    at com.google.net.async.EventDispatcher.loop(EventDispatcher.java:101)
    at com.google.net.rpc.RpcService.runUntilServerShutdown(RpcService.java:251)
    at com.google.apphosting.runtime.JavaRuntime$RpcRunnable.run(JavaRuntime.java:374)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ArrayStoreException: [Ljava.lang.String;
    at java.io.ObjectInputStream.readArray(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.defaultReadObject(Unknown Source)
    at org.apache.wicket.Component.readObject(Component.java:4465)
    at sun.reflect.GeneratedMethodAccessor6.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at java.io.ObjectStreamClass.invokeReadObject(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readArray(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readArray(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readArray(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readObject(Unknown Source)
    at java.util.HashMap.readObject(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at java.io.ObjectStreamClass.invokeReadObject(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
    at java.io.ObjectInputStream.readSerialData(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readObject(Unknown Source)
    at com.google.apphosting.runtime.jetty.SessionManager.deserialize(SessionManager.java:358)
    at com.google.apphosting.runtime.jetty.SessionManager.loadSession(SessionManager.java:280)
    at com.google.apphosting.runtime.jetty.SessionManager.getSession(SessionManager.java:255)
    at org.mortbay.jetty.servlet.AbstractSessionManager.getHttpSession(AbstractSessionManager.java:237)
    at org.mortbay.jetty.Request.getSession(Request.java:998)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:192)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
    at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:237)
    ... 27 more

2009年7月27日月曜日

#appengine 用のmavenプラグインをいくつか1.2.2対応した

以下の二つのエントリで紹介したプラグインを1.2.2対応しました。どちらも、1.2.2-SNAPSHOTというバージョンでdeployしてあります。

gae-maven2-plugin
gae-wicket-quickstart

Wicketの方は、以前のエントリで紹介した、自社で作業しているWikiの内容をいくらか反映したものになっています。詳しくしりたい場合は変更内容の確認をどーぞ。

2009年7月22日水曜日

#AppEngine for Javaで全文検索する

Compassを使って全文検索を試していたので、Wikiにまとめました。

AppEngine上ではWicketも使っていて、その場合はWicket標準のFileUpload等でも問題が出るためそのあたりにも触れていたりします。

TOPGATEという文字がありますが、これは自分が勤務している会社です。会社からの情報公開という意味も兼ねて、AppEngine関連で調査した内容等を公開していくのに上記Google Sitesを使っても良いという事になりました。ついでに、AppEngine for Javaが公開されてから自分が行った作業なんかもまだまとめ作業ができてないので、それらも全部上記サイトに整理して書いていこうかと考えてます。とりあえず追記したいのはmaven関連、Datastore関連かなー。

こんなカンジに、仕事として毎日 App Engine, Wicket を触っていられる環境なワケです、羨ましいでしょ〜。

2009年7月19日日曜日

#appengine のテスト用初期データを作成する

AppEngine用のプロジェクトをSDK上で起動してIntegration-Testしたり、Eclipse上のSDKで起動して手で動作確認したりするときのDatastoreの初期データをどーするか?といった話です。

単体テスト環境で使うDatastoreの初期化をちょっと弄ってやるだけで良かったよぅです。個人的にはずっとうまくいかずに悩んで、プロダクトコードに"ローカル環境で起動したときだけ初期データを作成する"を含めてしまうという馬鹿な事をやっていたけど、やっと初期データの作成方法がわかったのでメモ。

必要な情報

まずは、ApplicationID。これはappengine-web.xmlで記述しているapplication要素の値でおk。もうひとつは、VersionIDなんだけど、自分がずっとはまっていたのはコレ。この値もappengine-web.xmlversion要素の値を使用すりゃいいと思ったけど、ここがちょっと違う。たぶんappengine-web.xmlversion要素の値+".1"なのかな。正確な値は、SDK上でWebアプリとして起動したタイミングで以下を実行した値を使用する。

  • ApiProxy.getCurrentEnvironment().getAppId()
  • ApiProxy.getCurrentEnvironment().getVersionId()

例えば自分の環境だとappengine-web.xmlではそれぞれ"shin1ogawa", "wicket-snapshot"という値を使っているけど、実際に必要な値はそれぞれ"shin1ogawa", "wicket-snapshot.1"とかだった。

テストデータ出力モジュール

正しいバージョン番号さえわかれば後は簡単。

テストデータ作成時に上記のsetUp()を実行し、テストデータ作成はfactory.getPersistenceManager()を使うなり低レベルAPIを使うなりして普通にDatastoreに書き込みを行い、最後にtearDown()を実行する。上記の場合はtarget/testData/WEB-INF/appengine-generatedフォルダ配下にlocal_db.binというファイルができるので、それをSDKで実行する際のフォルダ(Google Plugin for Eclipseを使っているなら、デフォルトは${project}/war/WEB-INF/appengine-generated)にコピーしてからSDKを起動すればおk!

自分の場合はこれも通常のTestCaseとして毎回作成する事にしている(上記メソッドを@BeforeClass, @AfterClass にして、setUp()内ではtarget/testDataフォルダを最初に削除したり。)。

2009年7月18日土曜日

#appengine のSLAについて

Google App Engine for Java の Google GroupのMLで定期的に流れている、IRC 上の Office Hour の議事録みたいなメールの中に面白い内容があった。

自分が興味を持ったのはこの中のまとめに出て来てる"有料サービス向けのSLAはあるの?"みたいな質問に対して"いずれSLAとプレミアムサポートオプションがAppEngineで有効になるかもよ。"みたいな回答がされていた点。今は特別そーいうのは無いんだけど、楽しみにして待つとしよう。

Q: Will there be an SLA for paid services? A: In time, an SLA and premium support options may be made available for App Engine. As of now, we generally offer the same support for all users -- we check the support groups daily and have forms set up for requesting more quota, reporting billing issues, etc.

2009年7月16日木曜日

#appengine for java sdk1.2.2への対応方法

14日の日本時間の午前中にGoogle App Engine BlogにてGoogle App Engine for Java 1.2.2をリリースしたという公式のアナウンスがあった。その日の夜くらいに確認すると、mvnsearchのリポジトリにも1.2.2対応のモジュールがアップロードされているのが確認できたので、動作確認を行っていた。が!!単体テスト周りが結構動かなくて苦労したw やっぱり、mavenとeclipse両方で動作させる事ができなきゃツライんで、そこを目指す人向けにメモを書いておく。

公式のアナウンスより

リリースノートのURLはhttp://code.google.com/p/googleappengine/wiki/SdkForJavaReleaseNotes。目玉としては、pythonのSDKにあるローカル環境でのDatastoreの確認を行う機能が搭載された事。これはありがたい。他にもdatanucleusの不具合対応の取り込みとか。あと、

Better emulate datastore transaction support in the DevAppServer.
ってのがあるので、Low-Level APIでのTransactionがそろそろサポートされたはずだと期待。Task Queue APIは入ってない。

Eclipseのプラグインは、メニューから[Help][Software Updates]で[Software Updatesand Add-ons]画面を開き、[Installed Software]からGoogle関連のモジュールを選択して[Update]すればおk。プロジェクト毎にSDKのバージョンを設定する事も出来るが、設定の[Google][App Engine]でデフォルトをどれにするか?を選択する事も出来る。ついでに書いておくと、Galileo対応のGoogle Plugin for Eclipseは来週くらいにリリースされそうなカンジだ。

sdk単体でのダウンロードURLは http://googleappengine.googlecode.com/files/appengine-java-sdk-1.2.2.zip日本語のダウンロードサイトは未だに1.2.0へのリンクがあるよぅだw

com.google.apphosting.api.ApiProxy.Environmentのインターフェースが変わっている

public void setDefaultNamespace(String s)が無くなって、Map getAttributes()ってのが増えてる。この件はappengine-java MLでも話題になっていたけど、中の人の返信があって、その内容で落ち着いたみたい。

JDOのEntityの状態が変わった

Query#execute()した直後のEntityの状態
  • 1.2.1: ObjectState.PERSISTENT_NONTRANSACTIONAL_DIRTY
  • 1.2.2: ObjectState.HOLLOW_PERSISTENT_NONTRANSACTIONAL
Transaction開始後に値を操作したEntityの状
    1.2.1: ObjectState.PERSISTENT_NONTRANSACTIONAL_DIRTY
  • 1.2.2: ObjectState.PERSISTENT_DIRTY

変わった、といぅよりは「正しくなった」という事なんでしょね。

低レベルAPIでTransactionがサポートされた

例えば、1.2.1までは次のように無理矢理transaction.rollback()してもロールバックは効かなかったが、1.2.2でよぅやく正しくロールバックされるよぅになっていた。

maven対応は…?

ちゃんとできる。が、ちょっと納得いかない点もある。まずは作業結果のリンクを貼っておく。

自分のJDOの動作確認用プロジェクトではJPAは使っていないんだけど、JPAのモジュールを含めておかないと次のようなExceptionが投げられる。

java.lang.RuntimeException: Unable to replace JPACallbackHandler.
at org.datanucleus.store.appengine.DatastorePluginRegistry.getExtensionPoint(DatastorePluginRegistry.java:66)
at org.datanucleus.plugin.PluginManager.getExtensionPoint(PluginManager.java:65)
at org.datanucleus.plugin.PluginManager.getConfigurationElementForExtension(PluginManager.java:113)
at org.datanucleus.plugin.PluginManager.getAttributeValueForExtension(PluginManager.java:230)
...

で、org.datanucleus:datanucleus-jpa:1.1.4:providedを追加する。maven的にはコレでokだけど、この状態で$ mvn eclipse:eclipseしてしまうと、eclipse側でテストを実行した時に次のようなExceptionが投げられる(根っこの原因を抜粋)。

org.datanucleus.exceptions.NucleusException: Plugin (Bundle) "org.datanucleus.jpa" is already registered. Ensure you dont have multiple JAR versions of the same plugin in the classpath. The URL "file:/dropins.ganymede/google/plugins/com.google.appengine.eclipse.sdkbundle_1.2.2.v200907131030/appengine-java-sdk-1.2.2/lib/user/orm/datanucleus-jpa-1.1.4.jar" is already registered, and you are trying to register an identical plugin located at URL "file:/Users/shin1/.m2/repository/org/datanucleus/datanucleus-jpa/1.1.4/datanucleus-jpa-1.1.4.jar."

これを回避する為に、pom.xmlのmaven-eclipse-pluginの定義で、configuration/excludes/excludeの値に org.datanucleus:datanucleus-jpaを追加してやるとおk。

mavenから実行する時のエンハンサとして使用するmaven-datanucleus-pluginのバージョンが1.1.3しか見つからなかったので1.1.3を使っていて、とりあえずそれでも問題は起こっていない。が、他のモジュールに合わせて1.1.4がリリースされるんじゃないかな。

2009-07-27追記

UnitTest中にUserServiceを使いたい場合は、TestEnvironment.javagetAuthDomain(), isLoggedIn(), getEmail()が値を返す必要があるので注意〜。

2009年7月5日日曜日

197X第2回 LT大会に参加した #197X

appengine/java用の開発環境を簡単に作れるよ!というLTを行ったが、デモの最後のmaven integration-testの実行中のappengine-java-sdkのzipファイルのダウンロードに時間がかかりすぎてしまって、その間にドラ!となっちゃったw 質疑応答中に完了していたっぽいので、20秒ほど足りなかったよぅだ。

皆のLT

やっぱ197XメンバのLTはどれも面白い。そんな中でLT大賞をあげたい!と思ったのが kousyou さんの暗渠ネタ。"暗渠"という単語は初めて聞いたけど、buzzってたし、今後大流行間違い無し!

ありがとう

参加された皆様、会場を提供してくださったグリー株式会社様、会場の温度調整をがんばったいちいくん、「作る」「営む」に励んでおられるござ先輩、2日後くらいに筋肉痛が来るはずでお気の毒ですなyamashiroさん、ドラで多数の心臓を止めかけたドラ娘、みんなありがとうございました!

あと、Jiemamyメンバがいつも下品なワケじゃないよ!普段はちゃんとマジメな話してますから!