火曜日, 5月 26, 2009
【Google App Engine】 開発で嵌らないためのTIPS
1.Eclipseでプロジェクト名を変更した場合は作業ディレクトリも変更する
2.Systemプロパティはappengine-web.xmlに記述する
3.DataNucleus Enhancerでエンハンスされているか常に気にしておく
4.LocalのDataStoreは掃除する
5.Localでは実行できるのにサーバにDeployできない場合はサーバのデータをClean upする
6.Logを参照することでサーバ上の動作確認をする
土曜日, 5月 23, 2009
【Google App Engine】 JDOから直接JSON、XML
GAEに対応したReflex Coreを公開
Entityとトランザクションでも触れたが、GAE対応したReflex Resource Mapperを公開したいと思う。Reflex Resource Mapperは、XML/JSON⇔JavaBeanのバインディングツールで、JDOと直接マッピングできる点がウリだ。デモサイトを見てもらうとわかるが、ブラウザからJSONを受け取ってJDOに変換してDatastoreに直接保存できる。この処理は以下の2行で書ける。
loginnew = (Login) mapper.fromJSON(req.getParameter("json"));
JdoUtils.insert(loginnew);
デモではJSONを受け取っているが、mapper.fromXML()とすることで同じ構造をしたXMLも受け取れる。
設定も簡単で、4つのjarファイルをGAEのアプリケーションに追加するだけですぐに使うことができる。また、特別な設定ファイルなどは必要ない。
Reflex Core Sample
デモサイト
SVN (Google code)
Reflexでは、RESTfulな新3層アーキテクチャや、APIをやめデータ構造に着目するなどでも説明しているとおり、Entityの設計に重きを置き、その構造のままEntityをCRUD(HTTPでGET/POST/PUT/DELETE)するといったRESTfulな設計を目指す。また、これを容易にするために、reflexentityeditorといったツールや、トランザクション処理に対応したReflexContainerなどを準備している。
JDO設計における注意点
私も嵌ったが親子関係をもつJDOの設計には注意が必要である。以下のようにBeanは基本的にEmbededでない限り、@PersistenceCapableと@PrimaryKeyの定義が必要である。(これは”おまじない”と思った方がいい)
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Content {
・・・
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
・・・
}
さらに、1対多の場合は、子(Content)に親(Login)のプロパティを定義する必要がある。
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Content {
・・・
@Persistent
private Login login;
・・・
}
<関連情報>
JDO problem with owned one-to-many relationships and superclasses
同一型の子Entityを持つと、子Entityの判断がつかない
GAE対応する際に苦労した点
ReflexをGAEに対応させるためにやったことは次のとおり。
1.Making XStream compatible with the Google App Engineを参考に、NoClassDefFoundErrorやExceptionInInitializerErrorをcatchして握りつぶす行を追加。
try {
registerConverter(new PropertiesConverter(), PRIORITY_NORMAL);
} catch (NoClassDefFoundError e) {
} catch (ExceptionInInitializerError e) {
}
2.sun.reflect.ReflectionFactoryを使ったクラスSun14ReflectionProvider.javaからPureJavaReflectionProvider.javaを使うように変更
3.一旦、JDOに保存されたものを取り出すと、java.util.ArrayListオブジェクトがorg.datanucleus.sco.backed.Listに勝手に変わるのでinstance ofが使えない。これはクラス名を指定することで使えるように修正した。
<関連>
JDOから直接SOAP、ATOM。それからDeep Copy
AJAX CRUDサンプルとJDO代替ライブラリ
Pagingをどうやって実現するか
RESTfulアプリのCRUDサンプル -Servlet編-
RESTfulアプリのCRUDサンプル -Modeling編-
金曜日, 5月 22, 2009
【Reflex】 段階的にScale可能なPDFサービス
最近、Amazonのクラウドサービスがすごい。Amazonクラウド、大規模データをHDD郵送で受け入れなんて、Amazonならではの強みを生かしたサービスといえよう。特に目をひいたのは、Amazon CloudWatch(インスタンス監視サービス)、Amazon Elastic Loadbalancer(ロードバランサー), Amazon AutoScaling(自動スケールアウト機構)などの機能で、これらを利用することで、負荷に応じて自動でマシンを起動する、AutoScalingという仕組みを作れるようになる。
ということで、Reflex itextとAutoScalingを使って、Scalable PDF Serviceというものを構築することにした。
前面にGAEを置いてバックエンドのEC2にディスパッチしている。GAEでデータの作成およびキャッシュ機能を持たせることにすることで、余分な負荷をなくすことができ、また懸案のレイテンシーも問題にならずにすむ。Amazon EC2は北米と欧州にあるので、どうしても「もっさり感」が出てしまうがGAEであればサクサクとレスポンスが返ってくる。GAEは制約が多くて使いづらい面も多いが、EC2と組み合わせることで、それぞれの強みを生かした非常によいサービスを提供できるようになる。
( Reflex itextも当初はGAE上で構築することを考えていたが、java.awt.*パッケージが使えないことで断念した。No Graphics? )
月曜日, 5月 11, 2009
【Google App Engine】 BigTableのJDBCラッパー jiql
GAE/JでSQLをどうしても使いたい方に朗報。jiqlというものを使えば、jdbc clientとしてBigTableに実際に保存されたデータにアクセスできるようになる。
これを使えるようにするには、jiqlのページから、jiql.jarとjiql.jspをダウンロードして、GAEアプリケーションに配置すればよい。それぞれ、WEB-INF/lib/の下と、./に置く。
ただし、jiql.jspにはBugがあり、jspコンパイル時にエラーとなるため、以下のように修正する。(try catchのスコープ外でString sql=""を定義する)
jiql testpage
jiql testpageでは、hostやuser、password、datasourcenameを入れる箇所があるが、とりあえず何も入れないで、exampleのsqlを入れてqueryボタンを押すだけで実行結果が見れる。
ただ、JOINが使えないのを見ればわかるように、まだクラウドでSQLを使うのは無理があるように思う。将来使えるようになったとしても、トランザクションやパフォーマンスのことを考慮すると、結局クラウドの作法に立ち戻るはめになるだろう。個人的には、トランザクションプロセッシングを司るアプリからはSQLを無理に使うべきでないと思うし、使ったところで結局はO/Rマッパが必要となるので嬉しくない。
SQLが真価を発揮するのは情報系データベースである。これは別システムとして立てることで、クラウドからデータを受け渡すようにすればよいと思う。
・・・
ResultSet result = null;
Connection Conn = null;
String status = "";
String url = "jdbc:jiql://local";
String host = "";
String password = "";
String user = "";
String dsName = "";
String sql ="";
if (request.getParameter("query") != null){
try{
sql = request.getParameter("sql");
if (sql == null || sql.length() < 1)
throw new SQLException("Please Enter a valid SQL Statement!");
Properties props = new Properties();
props.put("user",user);
props.put("password",password);
Class clazz = Class.forName("org.jiql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
Conn = driver.connect(url,props);
Statement Stmt = Conn.createStatement();
Stmt.execute(sql);
result = Stmt.getResultSet();
status = sql + "
SQL COMPLETED SUCESSFULLY";
}catch (Exception ex){
status = "" + sql + "
SQL FAILED " + ex.toString() + "";
ex.printStackTrace(response.getWriter());
}
//Conn.close();
}
・・・
以下同じ
登録:
投稿 (Atom)