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

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);
無事アップロードできるようになりました。
関連記事

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月6日木曜日

Bloggerでのデータファイル置場に関する検証と考察

Googleのブログサービス、BloggerではJavaScript、スタイルシート等の静的ファイルをサーバにアップロードする機能が無い。
つまり、これらのファイルは別のサーバにホスティングする必要がある。
ホスティングするサーバに関して検証を行い考えてみた。
あくまで個人的な考えです。

発端

私は今までGoogle App Engineにファイルをホスティングしていたのだが、先日(といっても結構前)、Google App Engineで数時間に及ぶ接続障害が発生し、私のブログが閲覧できない状態になってしまった。
これは、head内でGoogle App Engineのファイルを参照していたため。

ホスティングサービス

Google Site
無料で利用でき、かつGoogleが提供しているサービスとして、Google Siteがある。
Google Siteでは、ファイルアップロード機能も用意されているため、Google Siteにアップロードし、そのファイルをBloggerにて参照するという方法。
アップロードもGUIが用意されているため比較的簡単にできる。
また、ファイルの世代管理も可能。
Google App Engine
こちらも無料で利用できるGoogleのサービス。
少々知識は必要だが、自由度も高く、アプリケーションも開発できる。
ファイルのアップロードは、コマンドラインやEclipseなどによるデプロイという方式をとるため、手間がかかる。
また、ときどき障害が発生し、ファイルが参照できなくなる。
ファイルが参照できないと、最悪の場合、Bloggerの閲覧が不可能になる。(head内でGoogle App Engineのファイルを参照している場合など)
オンラインストレージ
Dropboxや、SugarSyncなどにファイルをアップし、パブリックリンク機能を利用する方法。
ファイル管理は楽にできる。
また、ファイルの世代管理も可能。
但し、レスポンスが悪い時もある。

検証

Google Site、Google App Engine、Dropbox、SugarSyncにファイルを置いて、レスポンス時間を計測。
head内でGoogle App Engineのファイルを参照するのは懲りたのだが、改めてどの程度の応答か確認してみる。
置くファイルは、100KB(102,400Byte)のJavaScriptファイル。
計測する環境は、以下
  • Windows 7 64bit
  • Google Chrome 23
計測は、Google Chromeのデベロッパーツール(Ctrl+Shift+I)のNetworkにて行う。
また、デベロッパーツールにて、ブラウザのキャッシュを無効化(Disable cache)した。
計測後、HARファイルとしてエクスポートし、集計しました。

単位はミリ秒です。
Google Site
(リダイレクト)
Google Site
(ファイル取得)
Google Site
(リダイレクト+ファイル取得)
Google App Engine SugarSync Dropbox
1回目 312 277 589 549 613 1295
2回目 342 269 611 512 595 1273
3回目 260 263 523 720 589 2043
4回目 271 439 710 559 565 1220
5回目 281 267 548 816 948 1463
6回目 324 240 564 669 757 1700
7回目 256 275 531 655 822 1596
8回目 294 572 866 562 1011 1336
9回目 295 315 610 561 804 1541
10回目 324 458 782 748 527 1323
平均値 296 338 633 635 723 1479
中央値 295 276 600 609 685 1400
最小値 256 240 523 512 527 1220
最大値 342 572 866 816 1011 2043
検証で分かったこと
Google Site
Google のサービス内(Blogger、Google App Engine等)からであれば、ファイルを読み込むことが可能。
ただし、直接読み込めるわけではなく、リクエスト»Auth keyの発行»リダイレクト»ファイル読込 となるため、2回目のリクエストでようやくファイルを読み込むことができる。
Google App Engineと比較すると、稼働率は高いと思う。
レスポンスで返されるファイルは、Content-Encoding:gzipとなっており、ファイルが圧縮されているため、通信量の節約が行われているので、レスポンスも早い。
Last-Modified,Etag,Cache-Control(max-age=1, must-revalidate)ヘッダによってキャッシュ制御が行われている点も良い。
Google App Engine
ファイルを直接読み込むことが可能。
Google Siteと同じようにContent-Encoding:gzipによってファイルを圧縮して返してくれる
しかし、Google Siteより稼働率は低いと思う。
ファイルに付与するヘッダや、キャッシュの有効期限を任意に設定できるという点で、自由度は高い。
SugarSync
ファイルを直接読み込むことが可能。
レスポンスは若干遅め。
Google Siteと同じようにContent-Encoding:gzipによってファイルを圧縮して返してくれる
Last-Modified,Etag,Vary(Accept-Encoding)ヘッダによってキャッシュ制御も行われる。
障害は少ない。
Dropbox
ファイルを直接読み込むことが可能。
レスポンスはかなり遅い。
なぜなら、Google Site、Google App Engine、SugarSyncと異なり、gzip圧縮してくれないため、通信量が多くなる。
他と比べて、倍の時間がかかっている。
Cache-Control:max-age=0となっているため、ファイルは常にサーバから取得される
レスポンスが遅いうえに毎回問い合わせとなるため、最悪。
障害は少ないがオススメしない。

総評

今回の検証ではGoogle Siteが一番良さげ。
早速head内で読み込んでいるファイルをGoogle App EngineからGoogle Siteに変更しました。
Dropboxは意外に残念な結果でした。

他に検証してほしいホスティングサービスがあれば、コメントにて教えてください。
ただし、パブリックリンクが取得できるホスティングサービスに限ります。
Google Drive、SkyDrive、Boxは現時点でパブリックリンクに対応していません。
関連記事

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年7月3日火曜日

[Pleiades] Eclipse 4.2 Juno Windows 32bit 日本語化手順

ここでは、プラグインの管理面から、Pleiadesに同梱されているreadme.txtの方法ではなく、dropinsフォルダにインストールする方法を紹介します。

準備

ダウンロード
以下のファイルをダウンロードする
Eclipse 4.2
Eclipse Downloads
http://www.eclipse.org/downloads/
Eclipse Classic 4.2からダウンロード
Pleiades
Eclipse 日本語化 | MergeDoc Project
http://mergedoc.sourceforge.jp/index.html#pleiades.html
Pleiades 本体ダウンロードからダウンロード
解凍
ダウンロード後、ドライブの直下(C:\等)に上記のファイルを解凍する。
解凍時の注意点は、Pleiades配布ページ等を参照。
Eclipseの配置
Program Files等に、eclipseフォルダを移動する。
この辺は好みで。
パスにマルチバイト文字を含んだりすると悪影響があるかもしれません。

日本語化

Pleiadesインストール
eclipseフォルダ配下のdropinsフォルダ配下に、MergeDoc/eclipseというパスを作成し、pleiadesフォルダ配下のfeaturespluginsをコピーします。
ディレクトリ構成はこんな感じ
eclipse
└─dropins
    └─MergeDoc
        └─eclipse
            ├─features
            │  └─jp.sourceforge.mergedoc.pleiades
            │      └─META-INF
            └─plugins
                └─jp.sourceforge.mergedoc.pleiades
                    ├─conf
                    │  ├─additions
                    │  └─props
                    ├─lib
                    │  ├─apache
                    │  └─javassist
                    └─META-INF
eclipse.exe -clean.cmdもeclipseフォルダ配下へコピーしておきます。
設定
eclipse.iniを開き、以下の行を最終行に追加する。
-javaagent:dropins/MergeDoc/eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar

完了

eclipse.exe を起動すると、Eclipseが日本語化されて起動します。

フォント変更

ここからはお好みで。
デフォルトのままだと、英数字と日本語文字のバランスが取れていないので、フォントを変更する。
  1. ウィンドウ設定で設定画面を開く。
  2. ツリーから、一般外観色とフォントでフォントを変更する。
私はメイリオをモノスペースに変換したMeiryoKe_Consoleを指定しました。
参考:プラグイン対応状況
私の以下の常用プラグインが4.2に対応しているか調べました。

オススメ プラグイン for Eclipse 3.7 - ログろいど
プラグイン名 対応状況 備考
JStyle × 未対応
64bit版のみ対応
AnyEdit 4.2は公式には未対応。
3.8向けのプラグインで動作を確認。
Google Plugin for Eclipse http://dl.google.com/eclipse/plugin/4.2
Android Developer Tools http://dl.google.com/eclipse/plugin/4.2
Google App Engine Java SDK http://dl.google.com/eclipse/plugin/4.2
JStyle以外は対応していました。
ただし、JStyleも近々対応するようですので、気長に待ちましょう。
直近は時間がとれませんが
JStyle 4.2 32bit版もリリースしたいと思います。
RE: JStyle 4.2.0.0 の 32bit 版は ? [#64426] - MergeDoc - SourceForge.JP
http://sourceforge.jp/forum/message.php?msg_id=64426
続報
JStyle 4.2 (32bit) がリリースされました!!
対応の早さに感謝です。
デフォルト機能の、スペース、タブ、改行コード可視化の機能でも良いんですけど、全角スペースを含めて可視化するにはやっぱりコレがベスト。
ダウンロード - MergeDoc - SourceForge.JP
http://sourceforge.jp/projects/mergedoc/releases/?package_id=12531
さらに続報
Pleiades All in One 4.2 32bit がリリースされました。
有難く使わせていただきましょう。
Pleiades - Eclipse プラグイン日本語化プラグイン | MergeDoc Project
http://mergedoc.sourceforge.jp/index.html#/pleiades_distros4.2.html
関連記事

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できるようになりました。
やはりタイミング障害だったみたいです。
関連記事

2012年6月26日火曜日

[GAE/J] Bloggerで@font-faceによるWeb Fontを利用する方法

Bloggerに@font-faceによるWeb Fontを利用しようと思ったのだが、思いのほか手間取ったのでメモ。
サーバ上にファイル置いたり、リクエストヘッダ弄ったりするのは分からない/めんどいという方は以下の方法をお試しあれ。
@font-faceによるWeb Fontの実装方法 (サーバへのフォント配置不要) - ログろいど

発端

Web Symbols typefaceという素晴らしいWeb Fontを見つけたので、ぜひBlogger上で利用したいと思った。
これがきっかけ。

Bloggerの制限と、@font-faceの制限

手間取るきっかけとなったBloggerと@font-faceの制限を以下にまとめる。
Bloggerの制限
Bloggerでは以下のような制限がある。
  • 画像ファイル以外のファイルをホスティングする事が出来ない
これは前から把握していた。
そこで、Blogger上からファイル置場として利用できる環境を探った。(Googleが提供しているサービスのみ)
以下がBloggerで利用できるファイルホスティングサービス。
  • Google Site
  • Google App Engine (GAE)
前々からGAEをファイル置場として利用しているので、今回もGAEをセレクトした。
早速試してみる。
ん・・・フォントがダウンロードされていない。
レスポンスヘッダを見ると、Content-Length 0となっている。
どういう事だろう・・・。
@font-faceの制限
@font-face側の制限かと思い、@font-faceについて調べた。
すると、@font-faceには以下の制限があることが判明。
4.8.1 Default same-origin restriction

User agents must implement a same-origin restriction when loading fonts via the @font-face mechanism. This restriction limits the loading of fonts for a given document to fonts loaded from the same origin. Fonts can only be loaded via the same host, port, and method combination as the containing document, using the origin matching algorithm described in the [HTML5] specification. The origin of the stylesheet containing @font-face rules is not used when deciding whether a font is same origin or not, only the origin of the containing document is used. The restriction applies to all font types.

Some implementers would prefer to define a new mechanism (tentatively named From-Origin) to control access to all resource types, in preference to the origin matching algorithm referred to here. As such, this subsection should be considered at risk for alteration if such an alternative mechanism is defined.

Given a document located at http://example.com/page.html, fonts defined with ‘src’ definitions considered cross origin must not be loaded:

/* same origin (i.e. domain, protocol, port match document) */
src: url(fonts/simple.ttf);                     
src: url(//fonts/simple.ttf);                     

/* cross origin, different protocol */
src: url(https://example.com/fonts/simple.ttf);              

/* cross origin, different domain */
src: url(http://another.example.com/fonts/simple.ttf); 

4.8.2 Allowing cross-origin font loading

User agents must also implement the ability to relax this restriction using cross-site origin controls [CORS]. Sites can explicitly allow cross-site downloading of font data using the Access-Control-Allow-Origin HTTP header.

If an alternative mechanism to control resource loading (such as the suggested From-Origin HTTP header) is specified, the appropriate mechanism to relax the default same-origin restriction for @font-face may also change. As such, this subsection should be considered at risk for alteration if such an alternative mechanism is defined.
CSS Fonts Module Level 3
http://www.w3.org/TR/css3-fonts/
お~。
クロスドメインの制約に引っかかっていたのか。
Access-Control-Allow-Originをレスポンスヘッダに付与して回避しろと。
なるほど。

GAEでのCORS実装

レスポンスヘッダに Access-Control-Allow-Origin を付けろと言われても、GAEの static file のレスポンスにヘッダを追加するなんて機能はGAEで実装されていない。
となると、Java等で、レスポンスヘッダに Access-Control-Allow-Origin を付与するプログラムを書くしかない。
ということで、GAE/Jでレスポンスヘッダに Access-Control-Allow-Origin を付けるプログラムを書いた。
Blogger上で試してみると・・・OK。Web Fontが表示されている。
Web Symbolsのサンプル
%&'()*+,-./01234567:;<>?@ABCDFHIJKLMNOPQRSTUVWXZ[\]_`abcdefghijklmnopqrstuvwxyz{}~²³¹×

GAE以外での回避方法

Google Siteにファイルを置いても、Google SiteはBloggerのドメイン外なので、クロスドメイン通信となる。
また、Google Site側では、レスポンスヘッダに Access-Control-Allow-Origin を付けてくれたりという親切なことはしてくれない。
よって、プログラムを自分で書く以外に方法はないと思います。

プログラミングが苦手という方は、以下の方法で、Base64エンコードした内容をソース中に埋め込むか、Access-Control-Allow-Origin をヘッダに付与して返してくれるようなサービスを利用することになります。
@font-faceによるWeb Fontの実装方法 (サーバへのフォント配置不要) - ログろいど
参考までに
利用して良いかは不明ですが、Web Symbols typeface にホストされているフォントファイルは、Access-Control-Allow-Origin を付けてくれるので、Blogger上で利用可能です。
サイト内に利用可等明示されていないので、作者に確認が必要かと思いますが。
許可を得たうえで使用する場合は以下のような感じ。
@font-face {
    font-family: 'WebSymbolsRegular';
    src: url('http://www.justbenicestudio.com/fonts/websymbols-regular-webfont.eot');
    src: url('http://www.justbenicestudio.com/fonts/websymbols-regular-webfont.eot?#iefix') format('embedded-opentype'),
         url('http://www.justbenicestudio.com/fonts/websymbols-regular-webfont.woff') format('woff'),
         url('http://www.justbenicestudio.com/fonts/websymbols-regular-webfont.ttf') format('truetype'),
         url('http://www.justbenicestudio.com/fonts/websymbols-regular-webfont.svg#WebSymbolsRegular') format('svg');
    font-weight: normal;
    font-style: normal;
}
関連記事