Adobe
製品
Creative Suiteファミリー
Photoshopファミリー
Acrobatファミリー
Flash Platform
Digital Marketing Suite
Digital Enterprise Platform
Digital Publishing Suite
その他の製品一覧
ソリューション
コンテンツオーサリング
教育
金融機関
デジタルマーケティングソリューション
その他のソリューション
ラーニング サポート ダウンロード 会社情報
ご購入
アドビストア安心のサポート& サービス
アカデミック版のご購入学生、教職員、個人
ライセンスのご購入企業、教育機関、官公庁
販売パートナー
検索
 
情報 サインイン
ようこそ、 カート 注文状況 ユーザー登録
マイアカウント
サインアウト
サインインの目的 お客様のアカウントや体験版ダウンロード、製品の拡張機能、コミュニティエリアへのアクセスなどを管理するため
Adobe
製品 セクション   検索  
ソリューション 会社情報
サポート ラーニング
サインイン ようこそ、 注文状況 ユーザー登録
Qty:
Subtotal
Checkout
Adobe Developer Connection / Flexデベロッパーセンター /

Flex/AIRハマり帳 ~第2回 Google App Engine for Java+BlazeDSでハマらない方法・後編~

著者 スティルハウス

  • 吉川和巳氏

作成日

4 June 2009

ページ ツール

Facebookでシェア
Twitterでツイート
LinkedInでシェア
ブックマーク
印刷

Tags

必要条件

ユーザーレベル

すべて

はじめに

Flex/AIR開発には、ところどころに「落とし穴」があります。過去3年間のFlex/AIR開発を通じて、筆者はそうした「落とし穴」にことごとくハマり、そのたびに「二度と繰り返すまい」との思いで要点をメモしてきました。本連載では、その筆者の「Flex/AIRハマり帳」をもとに、これからFlex/AIR開発を始める方が同じ過ちを繰り返さないためのささやかなtipsを紹介していきたいと思います(なお、ここで紹介するtipsは、あくまで筆者の経験に基づいて得られたものであり、アドビシステムズの公式な提供情報ではないことにご留意ください)。

BlazeDSの本番環境へのデプロイでハマる

前回も説明したとおり、筆者は連休中に「Google App Engine for Java(GAE/J)とFlexでスケジュール共有ツールを作る」ことを思い立ち、「ご都合.com」を作成しました。

図:ご都合.com

図:ご都合.com

このご都合.comの特徴は、GAE/Jで動作するAdobe BlazeDSによって、FlexのRemoteObjectを利用したリモーティングによる通信を用いている点です。前回は、BlazeDSの各種ライブラリファイルや設定ファイルをGAE/JプロジェクトのWEB-INFフォルダ以下に配置し、ローカル環境で問題なくテストできたところまで説明しました。

しかし問題は、GAE/Jの本番環境へのデプロイ時に起こりました。GAE/Jプラグインの「デプロイ」ボタンをクリックしてコードをデプロイし、FlexクライアントからGAE/Jの本番環境へ接続してリモート呼び出しをテストすると、以下のようなエラーがGAE/Jのダッシュボード上に表示されました。

**** MessageBrokerServlet failed to initialize due to runtime exception: Error: java.lang.NoClassDefFoundError: java.lang.management.ManagementFactory is a restricted class. Please see the Google App Engine developer's guide for more details.
図:GAE/Jのダッシュボード上に表示されたエラーログ
図:GAE/Jのダッシュボード上に表示されたエラーログ

これはどうやら、MessageBrokerServletが利用する管理用APIが、GAE/Jのサンドボックスの制限に引っかかっているようです。「もしかしてBlazeDSってGAE/Jでは動かない……?」と不安になりつつも、ネット検索で先人たちの情報を探しました。すると、Martin Zoldano氏のすばらしいブログ記事を見つけました。

「AppEngine & Adobe BlazeDS (fix)」
このブログ記事を要約すると、「BlazeDS Community release 3.2.0.3978をGAE/Jで動作させることができた(ただしremoting機能のみ)。以下の3つの点の修正が必要となる」

  1. 管理機能のオフ(上述のエラーの原因)
  2. AbstractAmfInput.javaクラスのソースコード修正
  3. BaseHTTPEndpointクラスのソースコード修正

では、これらの手順を以下に説明します。ただし、ここで紹介するのは要約ですので、詳しくは上記ブログを参照してください。

まずは、管理機能をオフにします。サーバ側のservices-config.xmlにて、system要素に以下の1行を追加します。

<manageable>false</manageable>

これにより、BlazeDSの管理機能がオフとなり、上述のエラーが発生しなくなりました。しかしこれだけではまだ動作しません。Flexクライアントからリモート呼び出しを実行すると、クライアント側で通信エラーが発生してしまいます(サーバ側にはエラーが記録されません)。この原因は、上記ブログによると、GAE/Jのサンドボックスによるリクエストデータの読み込み動作が通常のサーブレットコンテナとは異なるため、例外を発生させているということのようです。

そこで、上記2の手順が必要です。これはBlazeDSのソースコードの修正ですので、それに先だってBlazeDSのダウンロードページよりソースコード(blazeds-src-3.2.0.3978.zip)をダウンロードし、付属のドキュメントにしたがってビルドする環境を整えます。ビルド可能な状態になったら、flex.messaging.io.amf.AbstractAmfInput.javaクラスの58行目を以下のように修正します。

this.in = new DataInputStream( new java.io.BufferedInputStream( in ) );

この修正後にビルドを実行し、それによって生成されたJARファイルを再度WEB-INF/lib以下にコピーしました。しかし、今度はBlazeDS側で「セッションが重複している」というエラーが発生してしまいます。この原因もはっきりとは分かりませんが、上記ブログによると、おそらくGAE/Jが自動的に複数のサーバノード上でサーブレットを展開するために、1つのクライアントからのセッション情報が重複したように見えてしまうという原因のようです。

これに対処するには、上記3の手順が必要です。flex.messaging.endpoints.BaseHTTPEndpointクラスのソースファイルを開き、setupFlexClientメソッドにてセッション重複エラーを発生させる部分(404行目~418行目)をコメントアウトし、BlazeDSを再度ビルドして、JARファイルを更新します。

以上の対処により、どうやらエラーを発生させずにBlazeDSをGAE/J上で動作させることができました。これらの手順を実施したのちは、まったくエラーも発生せずにスムーズに動作しています。上記ブログを書かれたMartin氏には本当に感謝したいと思います。

Datastore APIの取り扱いでハマる

BlazeDSは動いたものの、GAE/Jにはもうひとつ落とし穴がありました。それは、GAE/JのデータストアであるBigTableにアクセスするためのAPI、「Datastore API」とBlazeDSの連携です。

GAE/Jでは、Java EE標準のJDO(Java Data Objects)をベースとする「Datastore API 」に基づいてコードを記述することで、BigTableを簡単にアクセスできます。例えば、ご都合.comにおいてユーザーが作成した個々のカレンダー情報は、GtgCalendarクラスというEntityクラスに保持されています。

@PersistenceCapable(identityType = IdentityType.APPLICATION) public class GtgCalendar { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true") private String key; @Persistent private String uid; @Persistent private String title; @Persistent private String comment; ...<以下、そのほかのフィールドやgetter/setterは省略>

このように、POJOクラスに「@Persistent」や「@PrimaryKey」といったアノテーションを記述することで、同クラスの内容をBigTableに保存可能になります。なお、BigTableでは事前にDBスキーマを定義しておく必要はなく、スキーマの変更も随時可能です。

このGtgCalendarに納められたカレンダー情報を保存するコードは、以下のように記述します(いずれもご都合.comで実際に動作しているコードです)。

// GtgCalendar「gc」を保存 PersistenceManager pm = pmf.getPersistenceManager(); try { pm.makePersistent(gc); } finally { pm.close(); }

このように、GtgCalenderをPersistenceManagerのmakePersistenceメソッドの引数として渡すだけで、その内容がBigTableに格納されます。ちょうどHibernateやRuby on RailsにおいてオブジェクトをDB保存する場合と同じ感覚で、ものの数行で記述できます。

一方、BigTableに保存されたデータを検索して取得するには、JDOで規定されたクエリ言語「JDOQL」を用いて、SQLライクなクエリを記述します。

// クエリを作成 PersistenceManager pm = pmf.getPersistenceManager(); Query query = pm.newQuery(GtgCalendar.class); query.setFilter("token == tokenParam"); query.declareParameters("String tokenParam"); // クエリを実行 List<GtgCalendar> results; GtgCalendar gc = null; try { results = (List<GtgCalendar>)query.execute(token); } finally { query.closeAll(); pm.close(); }

ここでは、GtgCalendarのtokenフィールドをキーにして、同オブジェクトを検索しています。これもきわめて容易に記述できます。

eager loadingはできない

さて、Datastore APIがHibernate等の一般のORMと異なる点は、「eager loadingできない」という点です。つまり、例えば1つのEntityクラス「Department」が複数のEntityクラス「Employee」と親子関係にあるとき、Hibernate等ではeager loading設定を行うことで、両テーブルを結合して「1件のDepartmentレコードとその下の10件のEmployeeレコード」を1回で取得することが可能です。

一方、RDBではないDatastore APIの場合はこれができません。よって、親オブジェクトから子オブジェクトをひとつひとつ参照していき、子オブジェクトをBigTableから読み出しておく必要があります。ご都合.comの例では、GtgCalendarの取得後、その子である複数のGtgResponse、さらに孫であるGtgAvailableRangeのフィールドを読み込むことで、必要なデータを手作業で読み込みしています(この実装はパフォーマンス的に改善の余地があるかもしれません)。

private void loadChildren(GtgCalendar gc) { for (GtgResponse gr : gc.getResponses()) { for (GtgAvailableRange gar : gr.getAvailableRanges()) gar.getKey(); } }

PersistenceManagerはクローズする

もうひとつ筆者がハマった点は、「必ずPersistenceManagerをクローズしてからオブジェクトをBlazeDSに渡す」という点です。PersistenceManagerは、Entityオブジェクトの保存に加えて、上述のような親オブジェクトから子オブジェクトへの参照取得(ナビゲーション)にともなうBigTableの読み込みを司ります。つまり、上記コードのfinally節にて記述されている「pm.close()」によってPersistenceManagerがクローズされるまでは、「EntityオブジェクトとPersistenceManagerがひも付いている状態」となります。

この状態のままEntityオブジェクトをBlazeDSのリモート呼び出しの戻り値として渡してしまうと、リモート呼び出し時に例外が発生してしまいます(これと同様のことはHibernateにてセッションをクローズしない場合にも起こります)。よって、リモート呼び出しの戻り値は、必ず上記finally節の外側で戻す必要があります。

主キーは「Key as Encoded String」にする

最後の落とし穴は、Entityクラスの主キーです。Datastore APIにて主キーを定義するもっとも簡単な方法は、long型のidフィールドを用意することです。そこで筆者は当初long型のidを用いていました。しかしこのタイプの主キーでは、上述したような親オブジェクトから子オブジェクトへのナビゲーションに対応していません。親から子へのナビゲーションを使うには、BigTableにおける主キーを表す「com.google.appengine.api.datastore.Key」型のフィールドを設ける必要があります。

しかし、これでも不具合があります。このKey型のフィールドは、BlazeDSにてAMFのシリアライズを実施すると、キーの内容が失われてしまうのです。そこで筆者はさらに修正を加えて、Datastore APIのドキュメント上で「Key as Encoded String」と分類されている方法で主キーを実装しました。これは、上記のKeyオブジェクトの内容をString型にエンコードする方法で、主キーのフィールドには以下のようなアノテーションを追加します。

@Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true") private String key;

これによって、やっとEntityクラスの主キーを無事にFlexクライアントにも渡すことが可能となりました。

以上、本稿では「ご都合.com」の開発を通じて筆者が得た「GAE/J+BlazeDS」のtipsを紹介しました。ここで紹介した落とし穴さえ避ければ、この2つの技術のコンビはじつに強力な生産性を発揮します。ぜひ皆さんもお試しください。

製品

  • Creative Suiteファミリー
  • Photoshopファミリー
  • Acrobatファミリー
  • Flashプラットフォーム
  • Digital Marketing Suite
  • Digital Enterprise Suite
  • Digital Publishing Suite
  • モバイルアプリ

ソリューション

  • カスタマーエクスペリエンスマネジメント
  • コンテンツオーサリング
  • デジタルマーケティング

業種別ソリューション

  • 教育
  • 金融機関

サポート

  • ヘルプ&サポート
  • 注文と返品
  • ダウンロードに関するヘルプ
  • ユーザー登録に関するヘルプ

ラーニング

  • ADC: Adobe Developer Center
  • Adobe TV
  • Design Magazine
  • Photoshop Magazine
  • Focus In

ご購入方法

  • アドビストア
  • アカデミック版のご購入
  • ライセンスのご購入

ダウンロード

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

会社情報

  • プレスルーム
  • パートナープログラム
  • 企業の社会的責任(英語)
  • 採用情報
  • 投資家の皆様へ(英語)
  • イベント&セミナー
  • Legal(英語)
  • お問い合わせ
国・地域および言語の選択 日本(変更)
国・地域および言語の選択 閉じる

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • España
  • France
  • Deutschland
  • Hrvatska
  • Ireland
  • Israel - English
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • Magyarország
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Österreich - Deutsch
  • Polska
  • Portugal
  • România
  • Россия
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Slovenija
  • Slovensko
  • Srbija
  • Suomi
  • Sverige
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • Pacific - English
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

当Webサイトをご利用のお客様は、利用条件およびプライバシーポリシー(2011年9月30日更新)にご同意いただいたものとみなされます。

Reviewed by TRUSTe: site privacy statement