ようへい
ラベル Java の投稿を表示しています。 すべての投稿を表示
ラベル Java の投稿を表示しています。 すべての投稿を表示

2018年12月5日水曜日

GAE/J の環境から、 Dropbox にファイルをアップロードする

Google App Engine / Java8 の環境で、ファイルをアップロードされた場合、アップロードしたファイルの置き場所をどうしようかと悩んでいた。
すると、Dropbox の API があるようだったので使用してみることにした。
なんとなく GAE の環境に対応していそう。
Dropbox Core SDK for Java 6+
https://github.com/dropbox/dropbox-sdk-java とりあえず、以下でアクセストークンを作成。
Generate an access token for your own account
https://blogs.dropbox.com/developers/2014/05/generate-an-access-token-for-your-own-account/ チュートリアルにあるように、以下のように実装。
DbxRequestConfig config = DbxRequestConfig.newBuilder(SystemProperty.applicationId.get()).build();
DbxClientV2 client = new DbxClientV2(config, <<DROPBOX_ACCESS_TOKEN>>);
client.files().uploadBuilder("/GAE/" + fileName).withMode(WriteMode.OVERWRITE)
    .uploadAndFinish(fileStream);
とりあえずデプロイしてGAE上で実行。
すると案の定エラーが。
error: com.dropbox.core.NetworkIOException: content.dropboxapi.com (Logger.java:58)
 at com.dropbox.core.v2.DbxRawClientV2.uploadStyle(DbxRawClientV2.java:259)
 at com.dropbox.core.v2.files.DbxUserFilesRequests.upload(DbxUserFilesRequests.java:2985)
 at com.dropbox.core.v2.files.UploadBuilder.start(UploadBuilder.java:154)
 at com.dropbox.core.v2.files.UploadBuilder.start(UploadBuilder.java:20)
 at com.dropbox.core.v2.DbxUploadStyleBuilder.uploadAndFinish(DbxUploadStyleBuilder.java:92)
GAEで開発してる人ならピンと来るはず。
GAE/Java 8では外部にアクセスする際に使用する、Javaネイティブの java.net.URL.openConnection などが無効になり、URLFetchServiceFactory を使う必要がある。
どうやらホスト名が引けず、エラーになっているようなので、何かしら GAE 用の処理を加えないと使えなさそう。
GitHub で、検索をかけるとソース上に使用方法が書いてあった。
dropbox-sdk-java/src/main/java/com/dropbox/core/http/GoogleAppEngineRequestor.java
https://github.com/dropbox/dropbox-sdk-java/blob/aa70590b492bc79a47c6eaf5bc667206ab51bfdd/src/main/java/com/dropbox/core/http/GoogleAppEngineRequestor.java#L25
/**
 * {@link HttpRequestor} implementation that uses Google App Engine URL fetch service.
 * You can only use this if your project includes the App Engine libraries.
 *
 * <p> If your app runs in Google App Engine, it is strongly recommended to use this Requestor to
 * ensure certificate validation is performed on all requests.
 *
 * <p> To use this, pass an instance to the {@link com.dropbox.core.DbxRequestConfig} constructor:
 *
 * <pre>
 *     DbxRequestConfig config = DbxRequestConfig.newBuilder("MyAppEngineApp/1.0")
 *         .withHttpRequestor(new GoogleAppEngineRequestor())
 *         .build();
 *
 *     String accessToken = ...;
 *     DbxClientV2 client = new DbxClientV2(config, accessToken);
 * </pre>
 */
なるほど。
.withHttpRequestor(new GoogleAppEngineRequestor()) が必要らしい。
これを加えて、先ほどのコードを修正。
DbxRequestConfig config = DbxRequestConfig.newBuilder(SystemProperty.applicationId.get())
        .withHttpRequestor(new GoogleAppEngineRequestor()).build();
DbxClientV2 client = new DbxClientV2(config, <<DROPBOX_ACCESS_TOKEN>>);
client.files().uploadBuilder("/GAE/" + fileName).withMode(WriteMode.OVERWRITE)
        .uploadAndFinish(fileStream);
無事アップロードできるようになりました。
関連記事

2014年2月26日水曜日

自分なりの Android 開発環境まとめ (2014/02/26版)

久しぶりにAndroidアプリの開発をしようと思い、イチから開発環境の構築をしたのでメモ。
なお、以下の情報はIntel製CPUを積んだPCでの環境構築を前提としている。

環境構築準備

以下のファイルを準備する。
Eclipse 最新版
Pleiades - Eclipse プラグイン日本語化プラグイン | MergeDoc Project
Android SDK Tools
Android SDK | Android Developers
DOWNLOAD FOR OTHER PLATFORMSをクリックし、SDK Tools OnlyRecommendedとなっているインストーラーをダウンロード
Java Development Kit (JDK) 6
Java Archive Downloads - Java SE 6
からJDK 6 をダウンロード。
ダウンロードにはOracleへの登録が必要。
JDK 1.7 は公式にはサポートされていないので、公式でサポートされているJDK 1.6 を使用する。
ダウンロード後、それぞれインストールしておく。

公式IDEの Android Studio はまだ成熟してる感じじゃないので今回はパス。

Android SDK Tools での環境設定

開発対象とするバージョンのSDK PlatformIntel x86 Atom System ImageGoogle APIsに加え、ExtrasAndroid Support LibraryGoogle USB DriverIntel x86 Emulator Acceletor (HAXM)をインストールする。
Intel x86 Emulator Acceletor (HAXM) のみマニュアルでのインストールが必要なので注意。
Android SDK Toolsインストールディレクトリ配下の、extras\intel\Hardware_Accelerated_Execution_ManagerIntelHaxm.exeがあるので、インストールする。
その後、Android Virtual Device Manager(AVD)を起動し、エミュレートするデバイスを作る。
Intel製CPUで快適にエミュレートされるよう、デバイス作成時、以下を設定する。
CPU/ABI
Intel Atom (x86)を選択
Emulation Options
Use Host GPUをチェック

Eclipse での環境設定

JStyle アンインストール
便利な反面、coreがアップデートした時にマニュアルアップデートが必要だったり面倒なのでアンインストールしておく。
これは好みで。
plugins ディレクトリーの以下 jar ファイルを削除。
  • jp.sourceforge.mergedoc.jstyle_x.x.x.x.jar
  • org.eclipse.swt.win32.win32.x86_xxx.xxx.jar
  • org.eclipse.swt.win32.win32.x86_source_xxx.xxx.jar
その後、オリジナルの jar をリネーム。
  • org.eclipse.swt.win32.win32.x86_64_xxx.xxx.jar.backup
    -> org.eclipse.swt.win32.win32.x86_64_xxx.xxx.jar
  • org.eclipse.swt.win32.win32.x86_64_source_xxx.xxx.jar.backup
    -> org.eclipse.swt.win32.win32.x86_64_source_xxx.xxx.jar
一旦Eclipseを-cleanで起動しなおす。
Plugin インストール
ウィンドウ»設定を開き、インストール/更新»使用可能なソフトウェア・サイトで以下を追加する。
ADT
https://dl-ssl.google.com/android/eclipse/
Andrey Loskutov
http://andrei.gmxhome.de/Eclipse/
その後、ヘルプ»新規ソフトウェアのインストールで、以下をインストール。
http://andrei.gmxhome.de/Eclipse/
  • AnyEditTools
https://dl-ssl.google.com/android/eclipse/
  • Android DDMS
  • Android 開発ツール
  • Android 階層ビュアー
  • Android トレースビュー
一旦Eclipseを-cleanで起動しなおす。
設定変更
ウィンドウ»設定を開き、一般»エディター»テキスト・エディター行番号の表示にチェック。
Java»インストール済みのJRE検索を押してJDK 1.6 を検索しチェックしておく。
Java»コンパイラーコンパイラー準拠レベル1.6にしておく。
こんなところです。
構築後に思い出しながら書いているので、忘れてる部分もあるかも。
関連記事

2013年2月19日火曜日

[GAE/J] Google App Engine Java SDK 1.7.5 がリリースされました

Google App Engine Java SDK 1.7.5 がリリースされました。 リリースノートは以下。
  • New instance classes F4_1G and B4_1G are now available. These instances have compute capacity equal to F4/B4 but with a maximum of 1G RAM instead of 512MB.
  • The DataNucleus plugin has been upgraded to 2.1.2.
  • The deprecated classes AddException, AddResponse, ListException, ListIndexesException, ListIndexesRequest, ListIndexesResponse, ListRequest, ListResponse, and RemoveException were removed from the Search API in the SDK. If your app references any of these classes, you must deploy a new version without these references before the next release of App Engine. If you do not do this, your app may stop working in production.
  • The Conversion API, which was decommissioned last release, has been removed from the SDK. In a future release, the API will be removed from the runtime and applications that attempt to use it may stop working. Applications in production that import the library should be fixed as soon as possible.
  • We are making Java 7 available as an experimental feature. We strongly encourage local and production testing for all existing applications. For more information, please visit: http://developers.google.com/appengine/docs/java/java7
  • The Channel API now has the ability to send channel messages from any app version or backend regardless of where the channel was created.
  • The URL Fetch service now supports PATCH method requests.
  • The Mail API can now send mail bounce notifications to the app. The notification will be delivered to /_ah/bounce if mail_bounce inbound services are enabled.
  • The Blobstore service now returns the created filename instead of the blobKey when using Cloud Storage
  • Fixed an issue with Datastore callback annotations not working when running locally on Windows.
SdkForJavaReleaseNotes - googleappengine - Google App Engine Java SDK Release Notes - Google App Engine - Google Project Hosting
http://code.google.com/p/googleappengine/wiki/SdkForJavaReleaseNotes
実験的ではあるようですが、Java 7ランタイムがサポートされたようです。
試しに、Javaコンパイラーの準拠レベルを1.7にしてdeployしてみましたが、問題なく動いているようです。
Java 7ランタイムを有効にすると、deploy時にコンソールにUsing java7 runtime: trueと出力されました。
Java 7 Considerations - Google App Engine — Google Developers
https://developers.google.com/appengine/docs/java/java7
関連記事

2013年2月13日水曜日

[GAE/J] Version not ready. でデプロイが失敗する場合

2013/2/2 GAEにプロジェクトをdeployしようとしたらVersion not ready.というエラーで失敗した。
今までデプロイできていたプロジェクトだったのだが、ソースに手を加えていたので、前回デプロイした時の状態にソースをrollbackしてdeployしてみた。
しかし、結果は変わらず。
以下のようなダイアログが表示される。
See the deployment console for more details
Unable to update app: Version not ready.
eclipseのコンソールには以下のメッセージが出力されていた。
------------ Deploying frontend ------------

Preparing to deploy:
 Created staging directory at: 'C:\Users\Logroid\AppData\Local\Temp\appcfg5512351679699059483.tmp'
 Scanning for jsp files.
 Scanning files on local disk.
 Initiating update.
 Cloning 37 static files.
 Cloning 117 application files.

Deploying:
 Uploading 0 files.
 Initializing precompilation...
 Deploying new version.

Verifying availability:
 Will check again in 1 seconds.
 Will check again in 2 seconds.
 Will check again in 4 seconds.
 Will check again in 8 seconds.
 Will check again in 16 seconds.
 Will check again in 32 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
 Will check again in 60 seconds.
  on backend null.
java.lang.RuntimeException: Version not ready.

Debugging information may be found in C:\Users\Logroid\AppData\Local\Temp\appengine-deploy1109906141324865915.log
コンソールに表示されたログの中身は以下
Unable to update:
java.lang.RuntimeException: Version not ready.
 at com.google.appengine.tools.admin.AppVersionUpload.commit(AppVersionUpload.java:572)
 at com.google.appengine.tools.admin.AppVersionUpload.doUpload(AppVersionUpload.java:143)
 at com.google.appengine.tools.admin.AppAdminImpl.doUpdate(AppAdminImpl.java:371)
 at com.google.appengine.tools.admin.AppAdminImpl.update(AppAdminImpl.java:53)
 at com.google.appengine.eclipse.core.proxy.AppEngineBridgeImpl.deploy(AppEngineBridgeImpl.java:433)
 at com.google.appengine.eclipse.core.deploy.DeployProjectJob.runInWorkspace(DeployProjectJob.java:148)
 at org.eclipse.core.internal.resources.InternalWorkspaceJob.run(InternalWorkspaceJob.java:38)
 at org.eclipse.core.internal.jobs.Worker.run(Worker.java:53)
その後も試行錯誤してみたが、他のプロジェクト(前回デプロイ時からソース等、無変更)もデプロイに失敗する事から、この事象はGoogle App Engine サーバ側の問題であると考えた。
Twitter等で検索しても、他にも同じような状態になっている人もいるようだった。
また、Google グループのフォーラムにも同じ状態になっている人が居た。
Eclipseのプラグインでアプリケーションをアップロードしていたのですが、2013/02/02の午後からアップロードができなくなりました。
Eclipseの環境の問題かもしれないとAppcfgに切り替えてアップロードを再度試みたのですがやはり同じ結果です。
エラー内容:
...
99% Will check again in 60 seconds.
2013/02/03 1:46:39 com.google.appengine.tools.admin.AppVersionUpload commit
致命的: Version still not ready to serve, aborting.
99%  on backend null.

java.lang.RuntimeException: Version not ready.
Unable to update app: Version not ready.

こちらの問題ではなくApp-Engin側のような気がします。
同じ現象が発生した方はいらっしゃいますか?
アップロードができない - Google グループ
https://groups.google.com/forum/#!topic/google-app-engine-japan/qeVkTzjqKbU
翌日、改めてdeployしたら、今度はすんなりdeployできました。
という事で、どうやらサーバ側の一時的な障害だったようです。

Version not ready.となったら、サーバ側が回復するのを待ちましょう。
関連記事

2012年12月14日金曜日

[GAE/J] Google App Engine Java SDK 1.7.4 がリリースされました

Google App Engine Java SDK 1.7.4 がリリースされました。
リリースノートは以下。
  • Traffic Splitting is now a GA feature.
  • Task Queue Statistics is now a GA feature.
  • Logs API now has the ability to fetch requests based on a list of request_ids. Currently, this only works in production and is not supported in dev_appserver.
  • The SDK now includes an IDE-friendly zip with the source for the App Engine APIs. The zip resides in <SDK_ROOT>/src/user
  • Additional support for Maven now exists through both Maven archetypes and a Maven plugin. Documentation for the new Maven support can be found at https://developers.google.com/appengine/docs/java/tools/maven
  • DISTINCT for Datastore queries is now available as an experimental feature.
  • The decommissioned Conversion API has been removed.
  • Various JSP improvements
    • JSPs are now compiled together as opposed to one by one. This improves the compilation process time.
    • JSP compiled classes are packaged in JAR files.
    • You can now avoid the upload of JSP source, which is typically not needed at runtime. This can be done via the new --delete_jsps option in the appcfg tool. By default, this option is not set.
    • You can now package all the WEB-INF/classes/* classes into jar files. This can be done via the new --enable_jar_classes option in the appcfg tools. By default, this option is not set.
  • Users can now set how many columns can be viewed in the Datastore Viewer via a drop-down menu which customizes the number of columns displayed.
  • Fixed an issue with BackendService.getCurrentInstance() returning thread ID instead of integer instance ID in production.
  • Fixed an issue with Datastore Backup failing when a schema has a very large number of properties.
  • Fixed an issue with users being unable to change Authentication Type after app creation in the Admin Console.
SdkForJavaReleaseNotes - googleappengine - Google App Engine Java SDK Release Notes - Google App Engine - Google Project Hosting
http://code.google.com/p/googleappengine/wiki/SdkForJavaReleaseNotes
今回のリリースでは、Traffic Splittingのサポート、Task Queueの統計機能の追加、Datastore Viewerでの表示カラム数を指定できる機能の追加などが行われています。
また、JSPに関するコンパイル時間短縮等の幾つかの修正や、廃止となったConversion APIが削除されているようです。
関連記事

2012年12月5日水曜日

[GAE/J] slim3環境下でのエラー対処法覚え書き

先日GAE/Jのプロジェクトをslim3に移行したのですが、その際に躓いたエラーの対処法を書いておきます。

NoClassDefFoundError

エラー内容
Error for /hogehoge
java.lang.NoClassDefFoundError: Could not initialize class org.slim3.datastore.Datastore
 at org.slim3.datastore.DatastoreFilter.doFilter(DatastoreFilter.java:69)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.slim3.controller.HotReloadingFilter.doFilter(HotReloadingFilter.java:192)
 at org.slim3.controller.HotReloadingFilter.doFilter(HotReloadingFilter.java:157)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:102)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
 at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
 at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:266)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 at org.mortbay.jetty.Server.handle(Server.java:326)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
 at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:146)
 at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:447)
 at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:454)
 at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:461)
 at com.google.tracing.TraceContext.runInContext(TraceContext.java:703)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:338)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:330)
 at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:458)
 at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)
 at java.lang.Thread.run(Thread.java:679)
対処法
GAE/Jに必要なjarがwar/WEB-INF/libに存在しない場合に発生する。
eclipseで、プロジェクトのプロパティ»Google»AppエンジンでSDKを選びなおし、war/WEB-INF/libに必要なjarを取得する。

ExceptionInInitializerError

エラー内容
Error for /hogehoge
java.lang.ExceptionInInitializerError
 at org.slim3.datastore.DatastoreFilter.doFilter(DatastoreFilter.java:69)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.slim3.controller.HotReloadingFilter.doFilter(HotReloadingFilter.java:192)
 at org.slim3.controller.HotReloadingFilter.doFilter(HotReloadingFilter.java:157)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:102)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
 at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
 at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:266)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 at org.mortbay.jetty.Server.handle(Server.java:326)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
 at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:146)
 at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:447)
 at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:454)
 at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:461)
 at com.google.tracing.TraceContext.runInContext(TraceContext.java:703)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:338)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:330)
 at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:458)
 at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)
 at java.lang.Thread.run(Thread.java:679)
Caused by: org.slim3.util.WrapRuntimeException: An error occurred while creating a new instance of the class(org.slim3.datastore.DatastoreDelegate). Error message: com/google/appengine/api/datastore/DatastoreServiceConfig$Builder
 at org.slim3.util.ClassUtil.newInstance(ClassUtil.java:96)
 at org.slim3.datastore.Datastore.delegateClass(Datastore.java:82)
 at org.slim3.datastore.Datastore.initialize(Datastore.java:60)
 at org.slim3.datastore.Datastore.<clinit>(Datastore.java:52)
 ... 33 more
Caused by: java.lang.NoClassDefFoundError: com/google/appengine/api/datastore/DatastoreServiceConfig$Builder
 at org.slim3.datastore.AsyncDatastoreDelegate.setUp(AsyncDatastoreDelegate.java:119)
 at org.slim3.datastore.AsyncDatastoreDelegate.<init>(AsyncDatastoreDelegate.java:93)
 at org.slim3.datastore.DatastoreDelegate.<init>(DatastoreDelegate.java:63)
 at org.slim3.datastore.DatastoreDelegate.<init>(DatastoreDelegate.java:53)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:532)
 at java.lang.Class.newInstance0(Class.java:372)
 at java.lang.Class.newInstance(Class.java:325)
 at org.slim3.util.ClassUtil.newInstance(ClassUtil.java:94)
 ... 36 more
Caused by: java.lang.ClassNotFoundException: com.google.appengine.api.datastore.DatastoreServiceConfig$Builder
 at com.google.appengine.runtime.Request.process-6576c13a5e193afa(Request.java)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
 at org.slim3.datastore.AsyncDatastoreDelegate.setUp(AsyncDatastoreDelegate.java:119)
 at org.slim3.datastore.AsyncDatastoreDelegate.<init>(AsyncDatastoreDelegate.java:93)
 at org.slim3.datastore.DatastoreDelegate.<init>(DatastoreDelegate.java:63)
 at org.slim3.datastore.DatastoreDelegate.<init>(DatastoreDelegate.java:53)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:532)
 at java.lang.Class.newInstance0(Class.java:372)
 at java.lang.Class.newInstance(Class.java:325)
 at org.slim3.util.ClassUtil.newInstance(ClassUtil.java:94)
 at org.slim3.datastore.Datastore.delegateClass(Datastore.java:82)
 at org.slim3.datastore.Datastore.initialize(Datastore.java:60)
 at org.slim3.datastore.Datastore.<clinit>(Datastore.java:52)
 at org.slim3.datastore.DatastoreFilter.doFilter(DatastoreFilter.java:69)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.slim3.controller.HotReloadingFilter.doFilter(HotReloadingFilter.java:192)
 at org.slim3.controller.HotReloadingFilter.doFilter(HotReloadingFilter.java:157)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
 at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 at org.mortbay.jetty.Server.handle(Server.java:326)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:454)
 at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:461)
 at com.google.tracing.TraceContext.runInContext(TraceContext.java:703)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:338)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:330)
 at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:458)
 ... 1 more
対処法
src/META-INF/jdoconfig.xmlが無ければ作る。
repackaged-appengine-jakarta-standard-1.1.2.jar をwar/WEB-INF/libにコピーし、ビルドパスに通す。
jdoconfig.xmlは以下を参考に。
JDO を利用したデータストアの使用 - Google App Engine — Google Developers
https://developers.google.com/appengine/docs/java/gettingstarted/usingdatastore?hl=ja
関連記事

2012年11月27日火曜日

[GAE/J] deploy 時の Conflict エラー対処

deploy をしようと思ったらこんなエラーがコンソールに出た。
Preparing to deploy:
 Created staging directory at: 'C:\Users\User\AppData\Local\Temp\appcfg3567309257153504140.tmp'
 Scanning for jsp files.
 Scanning files on local disk.
 Initiating update.
com.google.appengine.tools.admin.HttpIoException: Error posting to URL: https://appengine.google.com/api/appversion/create?app_id=hoge&version=1&
409 Conflict
Another transaction by user hogehoge is already in progress for app: hoge, version: 1. That user can undo the transaction with "appcfg rollback".
appcfg rollback をしてトランザクションをロールバックしろと。
手順はこんな感じ。
>cd "C:\Program Files\eclipse_4.2\plugins\com.google.appengine.eclipse.sdkbundle_1.7.3\appengine-java-sdk-1.7.3\bin"
>appcfg.cmd rollback "D:\workspace\hoge\war"
Reading application configuration data...
11 27, 2012 10:38:01 午後 com.google.apphosting.utils.config.AppEngineWebXmlReader readAppEngineWebXml
情報: Successfully processed D:/workspace/hoge/war\WEB-INF/appengine-web.xml
11 27, 2012 10:38:01 午後 com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
情報: Successfully processed D:/workspace/hoge/war\WEB-INF/web.xml
11 27, 2012 10:38:01 午後 com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
情報: Successfully processed D:/workspace/hoge/war\WEB-INF/cron.xml
11 27, 2012 10:38:01 午後 com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
情報: Successfully processed D:/workspace/hoge/war\WEB-INF/queue.xml
11 27, 2012 10:38:01 午後 com.google.apphosting.utils.config.IndexesXmlReader readConfigXml
情報: Successfully processed D:/workspace/hoge/war\WEB-INF/datastore-indexes.xml
Beginning server interaction for hoge...
0%  on backend null.
Email: ******@gmail.com
Password for ******@gmail.com:
Success.
Cleaning up temporary files...
これで deploy できるようになりました。
関連記事

2012年11月1日木曜日

Google Play での 通貨表記について

Google Playにアクセスするツールを作っていて行き詰ったので、メモ。

¥表記で価格が取れない

アメリカにサーバを置いているクラウドサービス上からGoogle Playにアクセスすると、通貨表記が USD になってしまう。
URLパラメタに hl=ja を追加しても JPY で表示できない。
IPアドレスで判定されてしまっているようだ。
インターネット上にもこのような情報が少ない為、Googleのサポートに聞いてみた。

通貨表記の条件

Googleからの回答によると、以下の条件で通貨表記を決定しているとの事。
  1. Googleアカウントにログインしており、アカウントが、Google Walletのアカウントを作成済みなら、Google Walletでの国の通貨表記
  2. 上記に該当しない場合は、SIMカードでの国コード。SIMカードが無い場合は、アクセス元のIPから判断した国
つまり、アメリカにサーバを置いているクラウドサービスからGoogle Playにアクセスすると、IPから判定されUSD表記になってしまうという事。
また、URLパラメタ等でJPY表記にすることを強制できないようです。

対処方法

結局、クラウド環境から、日本のプロクシを介してGoogle Playにアクセスするという方法にしました。
USDをJPYに変換しようとも思ったのですが、変換結果がGoogle PlayでのJPYの値と異なるとまずいので・・・。
URLパラメタで制御できたりすれば一番良いんですけど。
関連記事

Androidアプリのセール情報チェックツール開発中!!

Androidアプリのセール情報チェックツールを開発しています。
現在クラウド環境にてデバッグ運用中です。
うまく動いている事が確認できましたら、セール情報をTwitter等でつぶやくようにしたいと思います。
そこで、セール価格をチェックしてほしいアプリ等ありましたら、コメントやTwitterにてGoogle Play上でのURLをご連絡いただきたいと思います。
アプリデベロッパーの方で、自分のアプリのセール情報をチェックしてほしい方も歓迎です。
実際にチェックツールでのチェックリストに追加するかは、こちらで判断します。
以下の条件にあてはまるアプリはチェックリストに追加しない可能性があります。
  • 無料アプリ
    チェックする意味が無い為、チェックリストから除外します。
  • インストール無料、アプリ内課金あり
    チェックツールで取得できるのはGoogle Playに表示された価格のみです。よって、アプリ内課金での価格は取得できません。誤解を招く事を避ける意味でチェックリストから除外します。
  • アダルトな内容のアプリ
  • 国や宗教の主張が含まれているアプリ
  • 著作権を侵害しているアプリ
    模造、キャラクターの無許可使用等
  • 評価が極端に低いアプリ
  • 評価数が少ないアプリ
  • ユーザに誤解を与えるアプリ
    実装されていない機能を謳っている詐欺アプリや、有名アプリに似せた名前のアプリ
  • その他、私個人の独断と偏見
チェックツールの大まかな仕様は以下です。
  • 1日4回チェック(登録アプリの増加によっては、回数を変更します)
  • チェック時間帯は原則、7時、12時、17時、22時
    いずれもJSTでの時刻
  • セールと判断する条件は、通常販売価格未満の価格であった場合
また、日毎に投稿しているアプリセール情報の記事に、チェックツールでのセール情報も記載する予定です。
関連記事

2012年10月24日水曜日

[GAE/J] Google App Engine Java SDK 1.7.3 がリリースされました

Google App Engine Java SDK 1.7.3がリリースされました。
リリースノートは以下。
  • We encourage you to try and test your application using Java 7 and the App Engine SDK. Note that Java 7 is not a supported runtime.
  • Java 7 Features that we encourage you to try in dev appserver:
    • Strings in switch
    • Binary integral literals and underscores in number literals
    • Multi-catch and more precise rethrow
    • Improved type inference for generic instance creation (diamond)
    • try-with-resources statement
    • Simplified varargs method invocation
  • Java 7 features that are NOT supported:
    • All new Java 7 classes (the Google App Engine whitelist has not been updated yet)
    • Method Handles
    • Invoke Dynamic bytecode
  • InetAddress name resolution now displays UnknownHostException instead of a runtime exception error when the host is unknown.
  • Datastore Index stats now report type instead of representation-type.
  • Class Loading Priority can now be granted to specific JAR files by adding class-loader-config element in appengine-web.xml file. This is an experimental feature.
  • Queries with transactions are now fully checked for consistency in Remote API.
  • Global Consistent Indexes are now deprecated in the Search API.
  • ListResponse<Document> listDocuments(ListRequest) has been deprecated and replaced with GetResponse<Document> getRange(GetRequest) in the Search API.
  • ListIndexesReponse listIndexes(ListIndexesRequest) has been deprecated and replaced with GetResponse<Index> getIndexes(GetIndexesRequest) in the Search API.
  • getField(String) has been deprecated and aliased to getFields(String) in the Search API.
  • AddResponse Index.add() has been deprecated and renamed to PutResponse Index.put() in the Search API.
  • RemoveResponse Index.remove() has been deprecated and renamed to DeleteReponse Index.delete() in the Search API.
  • Users can now change authentication options after app creation
  • Fixed an issue with Channel API breaking when using urlfetch service. This issue was limited to the SDK.
  • Fixed an issue with ResponseRewriterFilter not being threadsafe when using Remote API.
SdkForJavaReleaseNotes - googleappengine - Google App Engine Java SDK Release Notes - Google App Engine - Google Project Hosting
http://code.google.com/p/googleappengine/wiki/SdkForJavaReleaseNotes
Java 7に関するアップデートのようです。
Java 7からサポートされているランタイムは未サポートとのことなので、Java 7にするのはもう少し待ったほうが良いのかなぁ。
関連記事

2012年9月20日木曜日

[GAE/J] Google App Engine Java SDK 1.7.2 がリリースされました

Google App Engine Java SDK 1.7.2がリリースされました。
リリースノートは以下。
SdkForJavaReleaseNotes - googleappengine - Google App Engine Java SDK Release Notes - Google App Engine - Google Project Hosting
http://code.google.com/p/googleappengine/wiki/SdkForJavaReleaseNotes
課金方法の変更や、Conversion APIの廃止(に伴う警告表示)、タスクキューの統計情報、その他バグ修正、仕様変更が主な内容です。
また、1.7.3以降でDatastoreの型名が変更になるようです。
eclipseにも更新が落ちてきたので、更新してみました。
特に問題は出ていません。
関連記事

2012年8月23日木曜日

[GAE/J] Google App Engine Java SDK 1.7.1 がリリースされました

Google App Engine Java SDK 1.7.1がリリースされました。
リリースノートは以下。
You can download the latest version of the SDK here

App Engine SDK - Release Notes
Version 1.7.1 - August 21, 2012

 The URLFetch API now supports multiple cookie headers.
 http://code.google.com/p/googleappengine/issues/detail?id=3379 
 You can download 90 days worth of Usage Reports for your application from the Billing History page of the Admin Console.
 Task Queue requests now include an X-AppEngine-TaskExecutionCount that counts the number of times a task was run by an instance.
 Added support for multiple files to be deleted using the Files API.
 Added support to delete files from Google Cloud Storage using the Files API.
 Added a REQUEST_LOG_ID to be written in the logs and as an environment variable. This can be used to later identifying that request in the application logs.
 The Memcache Viewer now supports namespaces.
 http://code.google.com/p/googleappengine/issues/detail?id=7245 
 The Mail API now supports the following headers for outgoing mail: List-Id, List-Unsubscribe, On-Behalf-Of, Resent-Date, Resent-From, Resent-To.
 http://code.google.com/p/googleappengine/issues/detail?id=2559
 http://code.google.com/p/googleappengine/issues/detail?id=7672 
 PyCrypto 2.6. is now supported in the Python 2.7 runtime.
 http://code.google.com/p/googleappengine/issues/detail?id=7884 
 NDB now throws a BadProjectionError when trying to perform a projection query on an unknown or unindexed property.
 NDB Tasklets use the namespace from when it was called, not the namespace from when it is performed.
 http://code.google.com/p/appengine-ndb-experiment/issues/detail?id=190 
 NDB Properties' string representation size is limited to the max string length for Blob and Text properties.
 Appstats provides an interactive shell for observing RPC behavior of calls immediately.
 Appstats now contains information about the cost of the RPCs made during the request.
 The Images API now supports specifying the default pixel color to fill in for image types that don't support transparent images, such as JPEG.
 We've added full asynchronous API support in the Images API.
 Added an error to the Images API to indicate when a blob being referenced is not found or not accessible.
 Made the Python search limits public in the Search API.
 Added namespace support for the Search API in the Python SDK.
 http://code.google.com/p/googleappengine/issues/detail?id=7670 
 Added support in the Search API for simple sorting in the SDK.
 http://code.google.com/p/googleappengine/issues/detail?id=7456 
 The Testbed testing framework now supports the Logservice API.
 Added support in the Files API to list Google Cloud Storage files.
 Improve the MapReduce File API Reader to support multiple files and file formats.
 Fixed an issue with the App Engine satisfaction survey in the Admin Console which was released in 1.6.6, which will be re-enabled in this release.
 Fixed an issue with Search API websafe cursors not being returned as a websafe value.
 Fixed an issue where SearchRequest was returning errors that weren't available as search.Error subclasses.
 Fixed an NDB issue where entities over 1000000 bytes where written to memcache, causing failure.
 http://code.google.com/p/appengine-ndb-experiment/issues/detail?id=193 
 Fixed NDB AutoBatcher error handling.
 http://code.google.com/p/appengine-ndb-experiment/issues/detail?id=196 
 Fixed an issue where Appstats assuming all objects have a class attribute resulted in an error.
 http://code.google.com/p/googleappengine/issues/detail?id=5668 
 Fixed an issue where there was unbalanced string quoting in Appstats formatting.
 http://code.google.com/p/googleappengine/issues/detail?id=5976 
 Fixed an issue where search indexes were not persisted in the SDK.
 http://code.google.com/p/googleappengine/issues/detail?id=6791 
 Fixed an issue where an error in formatter.py in Appstats was being thrown.
 http://code.google.com/p/googleappengine/issues/detail?id=7034 
 Fixed an issue in the SDK where the Search API did not support boolean queries on a field.
 http://code.google.com/p/googleappengine/issues/detail?id=7247 
 Fixed an issue where numeric and date comparators were not supported in the Search API in the Python SDK.
 http://code.google.com/p/googleappengine/issues/detail?id=7247 
 Fixed an issue where Appstats didn't show a detailed view of an RPC when using the Python 2.7 runtime.
 http://code.google.com/p/googleappengine/issues/detail?id=7558 
 Fixed a search issue where snippets failed to highlight when a capital letter is used in a search term.
 http://code.google.com/p/googleappengine/issues/detail?id=7665 
 Fixed an issue where the SDK Admin tool couldn't browse search indexes containing unicode characters.
 http://code.google.com/p/googleappengine/issues/detail?id=7791 
 Fixed an issue where traffic splitting by cookie was not working.
 http://code.google.com/p/googleappengine/issues/detail?id=7813 
 Fixed an issue where RequestTooLargeError was being incorrectly thrown using Python 2.7.
 http://code.google.com/p/googleappengine/issues/detail?id=7820 
 Fixed a memcache race condition.
 http://code.google.com/p/googleappengine/issues/detail?id=7864 
 Fixed a Search issue in the SDK where a query that does not match all search terms resulted in divide-by-zero.
 http://code.google.com/p/googleappengine/issues/detail?id=7943
SdkReleaseNotes - googleappengine - Google App Engine Python SDK Release Notes - Google App Engine - Google Project Hosting
http://code.google.com/p/googleappengine/wiki/SdkReleaseNotes
Eclipseの更新サイトにはまだアップされていないようです。
関連記事

2012年7月6日金曜日

[GAE/J] Twitter4Jで A JSONArray text must start with ... が発生した場合

Google App EngineでTwitterのBotを運用しているのですが、とある処理でエラーが発生していました。
スタックトレースは以下。
twitter4j.TwitterRuntimeException: A JSONArray text must start with '[' at 2 [character 3 line 1]Relevant discussions can be on the Internet at:
 http://www.google.co.jp/search?q=610d24cf or
 http://www.google.co.jp/search?q=03587c2f
TwitterException{exceptionCode=[610d24cf-03587c2f 2efc4644-56189413], statusCode=-1, retryAfter=-1, rateLimitStatus=null, featureSpecificRateLimitStatus=null, version=2.2.5}
 at twitter4j.internal.json.LazyIDs.getTarget(LazyIDs.java:52)
 at twitter4j.internal.json.LazyIDs.getIDs(LazyIDs.java:59)
 at com.example.Hoge.doGet(Hoge.java:175)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
 at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
 at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:102)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
 at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
 at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:249)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 at org.mortbay.jetty.Server.handle(Server.java:326)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
 at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:135)
 at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:477)
 at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:449)
 at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:455)
 at com.google.tracing.TraceContext.runInContext(TraceContext.java:695)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:333)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:325)
 at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:453)
 at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)
 at java.lang.Thread.run(Thread.java:679)
どうやらTwitterのAPIから返されるJSONデータのパースに失敗しているようです。
Twitter側がおかしなデータを返すことはないと思うので、原因はTwitter4J、GAEにあると考えました。
また、ローカルでは実行できることから、GAEのライブラリではなく、GAEの環境に問題があると思われました。
Google 先生にエラーメッセージを聞いても明確な答えが返ってきません。
そして、JSONの結果をパースする際に影響するような設定がTwitter4Jに無いか調査しました。
TFJ-131 use GZipInputStream to handle gzipped response ? 9888b96 ? yusuke/twitter4j ? GitHub
https://github.com/yusuke/twitter4j/commit/9888b96f5eb0de60f189bf6088976119d145b512 個人的に、Accept-Encoding:gzip を指定して、壊れたデータが返ってきたり、gzipデコードがちゃんと行われなくて、レスポンスデータが受け取れなかったりという事があったので、これが気になった。
どうやら、レスポンスデータの受け取りをgzip圧縮する機能はTwitter4J 2.0.2にて実装されたらしい。
この機能は無効にできないのかなぁ。無効にして試したいんだけど・・・。

ソースの調査

Twitter4Jのページを見ても詳しい情報が無いので、ソースを覗いてみることにしました。
    public ConfigurationBuilder setGZIPEnabled(boolean gzipEnabled) {
        checkNotBuilt();
        configurationBean.setGZIPEnabled(gzipEnabled);
        return this;
    }
        if (notNull(props, prefix, HTTP_GZIP)) {
            setGZIPEnabled(getBoolean(props, prefix, HTTP_GZIP));
        }
    public static final String HTTP_GZIP = "http.gzip";
なるほど。
twitter4j.propertiesで有効/無効を制御できるようです。

いざDeploy

以下のように設定してみました。
twitter4j.http.gzip=false
これでDeployしてみると、見事エラーは無くなりました

衝撃の事実

って、githubのTwitter4Jのリポジトリ見たら、以下のコミットログがあった。
FIX: bug when the content-encoding: gzip header not interpreted ? 2b12d03 ? yusuke/twitter4j ? GitHub
https://github.com/yusuke/twitter4j/commit/2b12d03d409fedc97022c7271c5d96180c44e309
github側では既知の障害だったようで、修正済みっぽいです。
試してみる
という事でTwitter4Jから該当の修正が取り込まれているであろう最新スナップショットビルドの2.2.6を取得し、試してみました。
gzip圧縮機能はデフォルト(有効)に戻して、いざDeploy!
ダメでした・・・orz
twitter4j.TwitterRuntimeException: A JSONArray text must start with '[' at 2 [character 3 line 1]Relevant discussions can be on the Internet at:
 http://www.google.co.jp/search?q=610d24cf or
 http://www.google.co.jp/search?q=03587c2f
TwitterException{exceptionCode=[610d24cf-03587c2f 2efc4644-56189413], statusCode=-1, retryAfter=-1, rateLimitStatus=null, featureSpecificRateLimitStatus=null, version=2.2.5}
 at twitter4j.internal.json.LazyIDs.getTarget(LazyIDs.java:52)
 at twitter4j.internal.json.LazyIDs.getIDs(LazyIDs.java:59)
 at com.example.Hoge.doGet(Hoge.java:169)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
 at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
 at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:102)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
 at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
 at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
 at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:249)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
 at org.mortbay.jetty.Server.handle(Server.java:326)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
 at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
 at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
 at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:135)
 at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:477)
 at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:449)
 at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:455)
 at com.google.tracing.TraceContext.runInContext(TraceContext.java:695)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:333)
 at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:325)
 at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:453)
 at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)
 at java.lang.Thread.run(Thread.java:679)
次期の正式版リリースまで待ってみましょう。
それまではgzip無効で。
関連記事

[GAE/J] 文字化けさせずにメールを送信する方法

Google App Engineでメールを送りたいというケースがあると思います。
今回は、メール送信でちょっとハマったので、メール送信方法について書きたいと思います。

失敗したケース

以下のクラスをインポートしてメールを送ろうとしました。
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
メール送信処理はこんな感じ。
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
Message msg = new MimeMessage(session);
try {
    msg.setFrom(new InternetAddress("hoge@example.com"));
    msg.addRecipient(Message.RecipientType.TO, new InternetAddress("hoge@example.com"));
    msg.setSubject("テスト");
    msg.setText("テストメールです");
    Transport.send(msg);
} catch (AddressException e) {
    e.printStackTrace();
} catch (MessagingException e) {
    e.printStackTrace();
}
これを実行してみると・・・
メール本文はちゃんとMIMEエンコードが行われている。
しかし、Subjectがエンコードされておらず、文字化けして見えてしまう。

う~ん。

調査

GAEでメールを送る場合、オーナー(もしくは、オーナーとして招待したユーザ)のメールアドレスから送る必要がある。
今回Fromに指定したアドレスはオーナーのアドレスであることはGAEの管理画面からも確認できている。
となると何が原因なのか。

GAEのメール送信について調べてみよう。
MailService (Google App Engine Java API)
https://developers.google.com/appengine/docs/java/javadoc/com/google/appengine/api/mail/MailService あ、そもそもメール送信に使うクラスが違っていたみたい。

リトライ

という事で、以下のクラスをインポート。
import com.google.appengine.api.mail.MailService.Message;
import com.google.appengine.api.mail.MailServiceFactory;
メール送信処理は以下のように変更
Message message = new Message();
message.setSender("hoge@example.com");
message.setTo("hoge@example.com");
message.setSubject(subject);
message.setTextBody(body);
try {
    MailServiceFactory.getMailService().send(message);
} catch (IOException e) {
    e.printStackTrace();
}
無事文字化けも無くメール送信が完了しました。
関連記事

2012年6月28日木曜日

[GAE/J] Google App Engine Java SDK 1.7.0 がリリースされました

Google App Engine Java SDK 1.7.0がリリースされました。
リリースノートは以下。
  • You can now configure your custom domain to serve HTTPS requests with App Engine. You can choose either an SNI, VIP, or SNI and VIP configuration. SNI costs $9/month for 5 certificates. A VIP costs $99/month.
  • Premier customers now have the option to create applications to be served from datacenters located in the European Union.
  • Developers can configure their HRD app to use Google's PageSpeed Service, which automatically speeds up serving of content for your application. The PageSpeed Service costs $0.39/gigabyte in addition to the normal App Engine bandwidth charges.
  • The Search API now contains support for storing and searching on GeoPoints.
  • The total size of all application versions is now limited to 1 GB. In the future, you'll be able to purchase additional storage for your application code.
  • Logs API calls are now $.12/gigabyte for all data read from the Logs API over the first 100MB.
  • You can now specify a time frame of up to 1 year for the retention of your application logs. All storage above 1 GB is billed based on the prices for logs storage.
  • You can now specify HTTP headers on static content for your application.
  • The HRD Blob Migration tool is now generally available.
  • After using the datastore backup utility, you can now restore that backup to a new app id.
  • It will not be possible to create new authorizations for M/S applications to access Cloud SQL instances.
  • You can now delete a Google Cloud Storage object using delete() in the Blobstore Service API.
  • You can now fetch a Google Cloud Storage object using fetch() in the Blobstore Service API.
  • You can now store keys for a Google Cloud Storage object in the datastore.
  • The BlobstoreService.createUploadUrl() call now works for Google Cloud Storage buckets.
  • You can now use ImageService.getServingUrl() and ImageService.deleteServingUrl() for Google Cloud Storage objects.
  • You can now specify your own Security Provider in Java.
  • The Java Datastore API now supports Or queries.
  • We've increased various Search API limits. New limits are a maximum of 1000 documents returned from search() or listDocuments(), a maximum of 1000 indexes returned from listIndexes(), and a maximum offset of 1000 for search() or listIndexes()
  • Fixed a javascript syntax error in the Admin Console.
  • Fixed an issue in the Search API tab of the Admin Console where it threw an error displaying non-ascii characters.
SdkForJavaReleaseNotes - googleappengine - Google App Engine Java SDK Release Notes - Google App Engine - Google Project Hosting
http://code.google.com/p/googleappengine/wiki/SdkForJavaReleaseNotes#Version_1.7.0_-_June_26,_2012
はやく Java 7 に対応しないかなぁ・・・。
しかし、今日一日Deployに失敗するんだけど、みんなでDeployしまくっててサーバの負荷が高いのかな。
以下のようなエラーでDeployできない。くそー。
レスポンスコードが5xx系なので、サーバ側の障害っぽいんだけど。
もうちょっと様子見るか・・・。
Unable to update:
com.google.appengine.tools.admin.HttpIoException: Error posting to URL: https://appengine.google.com/api/appversion/create?app_id=hoge&version=x&
500 Internal Server Error

<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>500 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered an error and could not complete your request.<p>If the problem persists, please <A HREF="http://code.google.com/appengine/community.html">report</A> your problem and mention this error message and the query that caused it.</h2>
<h2></h2>
</body></html>

 at com.google.appengine.tools.admin.AbstractServerConnection.send1(AbstractServerConnection.java:285)
 at com.google.appengine.tools.admin.AbstractServerConnection.send(AbstractServerConnection.java:245)
 at com.google.appengine.tools.admin.AbstractServerConnection.post(AbstractServerConnection.java:224)
 at com.google.appengine.tools.admin.AppVersionUpload.send(AppVersionUpload.java:641)
 at com.google.appengine.tools.admin.AppVersionUpload.beginTransaction(AppVersionUpload.java:447)
 at com.google.appengine.tools.admin.AppVersionUpload.doUpload(AppVersionUpload.java:124)
 at com.google.appengine.tools.admin.AppAdminImpl.doUpdate(AppAdminImpl.java:328)
 at com.google.appengine.tools.admin.AppAdminImpl.update(AppAdminImpl.java:52)
 at com.google.appengine.eclipse.core.proxy.AppEngineBridgeImpl.deploy(AppEngineBridgeImpl.java:407)
 at com.google.appengine.eclipse.core.deploy.DeployProjectJob.runInWorkspace(DeployProjectJob.java:148)
 at org.eclipse.core.internal.resources.InternalWorkspaceJob.run(InternalWorkspaceJob.java:38)
 at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
Unable to update:
com.google.appengine.tools.admin.HttpIoException: Error posting to URL: https://appengine.google.com/api/appversion/startserving?app_id=hoge&version=x&
503 Service Unavailable

Application Busy (503)
Multiple clients are trying to modify the same application.

 at com.google.appengine.tools.admin.AbstractServerConnection.send1(AbstractServerConnection.java:285)
 at com.google.appengine.tools.admin.AbstractServerConnection.send(AbstractServerConnection.java:245)
 at com.google.appengine.tools.admin.AbstractServerConnection.post(AbstractServerConnection.java:224)
 at com.google.appengine.tools.admin.AppVersionUpload.send(AppVersionUpload.java:619)
 at com.google.appengine.tools.admin.AppVersionUpload.startServing(AppVersionUpload.java:600)
 at com.google.appengine.tools.admin.AppVersionUpload.commit(AppVersionUpload.java:545)
 at com.google.appengine.tools.admin.AppVersionUpload.doUpload(AppVersionUpload.java:141)
 at com.google.appengine.tools.admin.AppAdminImpl.doUpdate(AppAdminImpl.java:340)
 at com.google.appengine.tools.admin.AppAdminImpl.update(AppAdminImpl.java:52)
 at com.google.appengine.eclipse.core.proxy.AppEngineBridgeImpl.deploy(AppEngineBridgeImpl.java:407)
 at com.google.appengine.eclipse.core.deploy.DeployProjectJob.runInWorkspace(DeployProjectJob.java:148)
 at org.eclipse.core.internal.resources.InternalWorkspaceJob.run(InternalWorkspaceJob.java:38)
 at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
Unable to update:
com.google.appengine.tools.admin.HttpIoException: Error posting to URL: https://appengine.google.com/api/appversion/deploy?app_id=hoge&version=x&
503 Service Unavailable

Try Again (503)
An unexpected failure has occurred. Please try again.

 at com.google.appengine.tools.admin.AbstractServerConnection.send1(AbstractServerConnection.java:285)
 at com.google.appengine.tools.admin.AbstractServerConnection.send(AbstractServerConnection.java:245)
 at com.google.appengine.tools.admin.AbstractServerConnection.post(AbstractServerConnection.java:224)
 at com.google.appengine.tools.admin.AppVersionUpload.send(AppVersionUpload.java:619)
 at com.google.appengine.tools.admin.AppVersionUpload.deploy(AppVersionUpload.java:575)
 at com.google.appengine.tools.admin.AppVersionUpload.commit(AppVersionUpload.java:536)
 at com.google.appengine.tools.admin.AppVersionUpload.doUpload(AppVersionUpload.java:141)
 at com.google.appengine.tools.admin.AppAdminImpl.doUpdate(AppAdminImpl.java:340)
 at com.google.appengine.tools.admin.AppAdminImpl.update(AppAdminImpl.java:52)
 at com.google.appengine.eclipse.core.proxy.AppEngineBridgeImpl.deploy(AppEngineBridgeImpl.java:407)
 at com.google.appengine.eclipse.core.deploy.DeployProjectJob.runInWorkspace(DeployProjectJob.java:148)
 at org.eclipse.core.internal.resources.InternalWorkspaceJob.run(InternalWorkspaceJob.java:38)
 at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
Unable to update app: Error posting to URL: https://appengine.google.com/api/appversion/deploy?app_id=hoge&version=x&
503 Service Unavailable

Try Again (503)
An unexpected failure has occurred. Please try again.


See the deployment console for more details
Unable to update app: Error posting to URL: https://appengine.google.com/api/appversion/deploy?app_id=hoge&version=x&
503 Service Unavailable

Try Again (503)
An unexpected failure has occurred. Please try again.
その後
1:15頃、Deployできるようになりました。
やはりタイミング障害だったみたいです。
関連記事