2013年05月10日

Google App Engine 上のフレームワーク

Python と Java を比較するシリーズをここまで書いてきましたが、 具体的なコードは、フレームワークの存在でがらっと変わってしまったりします。 そこで、いくつかのフレームワークを紹介しておくことにします。

今回この記事を書くためにいろいろ検索してみたのですが、 開発が止まってしまったプロジェクトも多く、淘汰が進んでいるのかもしれません。

Python の Google App Engine 専用フレームワーク

Kay
テンプレートエンジン Jinja2 や WSGI ユーティリティの werkzeug などをまとめた Django 風フレームワークです。
Ferris
2013 年に登場した新しいフレームワークです。

Java の Google App Engine 専用フレームワーク

Slim3
Google App Engine for Java と言うと必ず名前の挙がるフレームワークです。
Objectify
フルスタックのフレームワークではなく、データアクセス部分を書きやすくするための API を提供するものです。
Gaelyk
少し毛色の変わったところで、JVM 言語 Groovy のフレームワークです。

一般的なフレームワークの使用

Python では WSGI に対応した各種のウェブアプリケーションフレームワークが Google App Engine で使われているようです。代表例は flask でしょうか。

Google App Engine for Java で動かせるフレームワークなどのリストがあります: WillItPlayInJava
参考にしてみて下さい。

ラベル:GAE
posted by mft at 17:34| Comment(1) | TrackBack(0) | 技術文書 | このブログの読者になる | 更新情報をチェックする

Python と Java 各種サービスの違い

Datastore 以外に、Google App Engine には各種のサービスが提供されています。 Google developers にある文書では左のリストの中に「Services」という項目が各言語にあります。 それを開いていただければ判りますが、Python でも Java でも基本的に同じサービスが提供されています。 しかし、その呼び出し方はそれぞれの言語で違いがあります。 ここでは代表例として Blobstore と Images を見てみましょう。

Python での Blobstore

Python では blobstore パッケージをインポートして使います。 あとはパッケージで定義された関数やクラスをごく普通に使うだけです。

from google.appengine.ext import blobstore
# key で指定された blob を取得する
blob_info = blobstore.BlobInfo.get(key)
# Blobstore に新しくアップロードする URL を生成する
blobstore.create_upload_url('/upload')

ただし、Blobstore に関しては、直接ウェブに口を設けるための blobstore_handlers というパッケージが別にあります。例えばダウンロード用のハンドラーは次のように用意します。

from google.appengine.ext.webapp import blobstore_handlers
class ServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
  def get(self, resource):
    ...

Java での Blobstore

Java では BlobstoreService というクラスのインスタンスを経由します。 関係するクラスは com.google.appengine.api.blobstore パッケージにあります。 通常、Servlet クラスの private フィールドとして

private BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();

のように初期化します。(XService インスタンスを XServiceFactory.getXService() で取得するこのパターンは他のサービスでもよく使われています。) あとは、以下のように blobstore オブジェクトのメソッドを呼び出してサービスを利用します。

// リクエストで受け取ったファイルを Blobstore に放り込む
Map blobs = blobstore.getUploadedBlobs(req);
// blobKey で指定された blob をレスポンスに返す
blobstore.serve(blobKey, res);
// Blobstore に新しくアップロードする URL を生成する
blobstore.createUploadUrl("/upload")

Python での Images

写真 original_image のサムネイルを作るのは以下のようにします。

from google.appengine.api import images
img = images.Image(original_image)
img.resize(width=80, height=100)
img.im_feeling_lucky()
thumbnail = img.execute_transforms(output_encoding=images.JPEG)

Java での Images

Python と同じように写真 original_image のサムネイルを作るのは以下のようにします。 com.google.appengine.api.images にある各クラスは import 済みだと思って下さい。

ImagesService imagesService = ImagesServiceFactory.getImagesService();
Image img = ImagesServiceFactory.makeImage(original_image);
Transform resize = ImagesServiceFactory.makeResize(80, 100);
Transform lucky = ImagesServiceFactory.makeImFeelingLucky();
Transform composite = ImagesServiceFactory.makeCompositeTransform().preConcatenate(resize).concatenate(lucky);
Image thumbnail = imageService.applyTransform(composite, img, new OutputSettings(JPEG));

ここでは ServiceFactory も大活躍です。

サードパーティ

Python では 多数のサードパーティ・ライブラリーが app.yaml に libraries を追加するだけで使用できます (Third-party Libraries in Python 2.7)。 Java にはそのような仕組みは導入されていません。以下の例では import django と記述したときに django 1.4 が使えるようにしています。

app.yaml 記述例:
libraries:
-name: django
 version: "1.4"
ラベル:GAE
posted by mft at 12:48| Comment(0) | TrackBack(0) | 技術文書 | このブログの読者になる | 更新情報をチェックする

Python と Java との Datastore 使用法の比較

Google App Engine を語る上でデータストアは避けて通れません。 軽く復習しておきましょう。 データストアは関係データベースではなく、データ取得のためのクエリにも様々な制限があります。 大雑把な理解としては、 関係データベースでテーブルに相当するものはカインド、 行(レコード)に当たるのはエンティティ、 列(カラム)に当たるのはプロパティです。

複数のプロパティが絡むクエリーを発行する際には、それ用のインデックスが必要です。 これは Python でも Java でも同じです。 このインデックスを定義するファイルは、Python では index.yaml、 Java では datastore-indexes.xml になります。 (繰り返しになりますが実は Java でも yaml 設定ファイルを使うことができるようです。 Java App Configuration Using app.yaml

Java には JDO、JPA といった Java 標準に従った別系統の API もありますが、 ここでは取り上げません。 書籍「Slim3 on Google App Engine for Java」によると、 遅くて使えないそうです。

Python でのエンティティの格納

Python でエンティティを扱う場合、Model クラスを継承してカインドごとにクラスを作成します。 Model クラスは google.appengine.ext.db にあります。 プロパティはクラス定義の際に、型に従った Property クラスを設定します。 こうして作ったクラスのインスタンスに各プロパティの値を与え、 最後に put メソッドを呼んでデータストアに格納します。

Java でのエンティティの格納

Java でエンティティを扱う場合、Entity クラスを使います。 Entity クラスは com.google.appengine.api.datastore にあります。 Entity クラスをインスタンス化する際に文字列としてカインド名を渡します。 プロパティの設定は setProperty メソッドにプロパティ名とプロパティ値を渡します。 プロパティ値は Object 型なので、何でも入れられます。 最後に DatastoreService インスタンスの put メソッドにエンティティを渡してデータストアに格納します。

Python におけるクエリー

Python においてクエリーの構築には2種類の方法があります。 一つはモデルクラスを経由する方法、もう一つは GQL を使う方法です。

モデルクラスを経由する方法では、格納に利用した Model を継承したクラスを使います。 これのクラスメソッド all を呼び出すと無条件にそのカインドのエンティティを取得するクエリーが生成されます。 このクエリーに filter メソッドで取得条件を追加したり、order メソッドで順番を設定したりします。

GQL はこういった操作をせずに、SQL 風のコマンドを記述して一気に望みのクエリーを構築します。

どちらの作り方をしたクエリーも、最後に run メソッドを呼ぶとイテレータを返すので、for 文などに渡して一つずつエンティティを取り出します。

Python コード例:
# クラス定義
class Person(Model):
  name = StringProperty()
  age = IntegerProperty()

# 格納
person = Person(name="Guido", age=57)
person.put()

# 取り出し
q = Person.all().fiter("age =", 57).order("+name")

for person in q.run():
  print person.name

Java におけるクエリー

Java では Query にカインド名を渡して Python の all 呼び出し相当のクエリーを作ります。 フィルターの追加は Query.Filter クラス(及びその派生クラス)を作って、setFilter メソッドで設定します。 一方、順序の設定は直接 addSort メソッドを使います。 こうして作ったクエリーを DatastoreService インスタンスの prepare メソッドで PreparedQuery に変換し、最後に asIterable メソッドを呼ぶことで Iterable を返すので for each で一つずつエンティティを取り出します。

Java コード例:
// DatastoreService インスタンスの取得
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

// 格納
Entity person = new Entity("Person");
person.setProperty("name", "Guido");
person.setProperty("age", 57);
datastore.put(person);

// 取り出し
Filter f = new FilterPredicate("age", FilterOperator.EQUAL, 57);
Query q = new Query("Person").setFilter(f).addSort("name", SortDirection.ASCENDING);
PreparedQuery pq = datastore.prepare(q);

for (Entity entity : pq.asIterable()) {
  System.out.println((String)entity.getProperty("name"));
}

index.yaml と datastore-indexes.xml

上記サンプルで必要になるインデックスです。

index.yaml
indexes:

- kind: Person
  ancestor: no
  properties:
  - name: age
  - name: name
    direction: asc

index.yaml は開発サーバーでアプリケーションを実行してみると自動で作られるので、 ほとんど手書きする機会はありません。

datastore-indexes.xml
<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="true">
    <datastore-index kind="Person" ancestor="false">
        <property name="age" direction="asc" />
        <property name="name" direction="asc" />
    </datastore-index>
</datastore-indexes>

datastore-indexes.xml の datastore-indexes タグの autoGenerate 属性は必須で、 true にしておくと開発サーバーで使ったインデックスが war/WEB-INF/appengine-generated/datastore-indexes-auto.xml に記録されていきます。ですので、ひと通りローカルでアプリケーションを実行したあと、 この内容を書き写せば OK です。必要なインデックスが揃ったら autoGenerate 属性を false に変えましょう。

次回予告

次回は各種サービスについて比較します。

ラベル:GAE
posted by mft at 11:44| Comment(0) | TrackBack(0) | 技術文書 | このブログの読者になる | 更新情報をチェックする