2011年2月27日日曜日

Channel API ドキュメント日本語要約 (Google App Engine)

 

概要

Channel APIはアプリケーションとgoogleサーバーの間に持続的な接続を作り、JavaScriptクライアントにポーリングを使わずにリアルタイムにメッセージを送ることができる。

Channel APIの要素

JavaScriptクライアント

クライアントの役割

  • チャネルに接続してサーバからチャネルのユニークなトークンを受け取る。
  • ほかのクライアントのアップデートを待ち受けて、データを使用する。
  • アップデートをサーバーに送信する。
サーバ

サーバの役割

  • 個々のJavaScriptクライアントにユニークなチャネルを作る。
  • ユニークトークンを作りJavaScriptクライアントに送る。
  • クライアントからのアップデートをPOSTで受ける。
  • チャネルを使ってクライアントにアップデートメッセージを送る。
クライアントID

クライアントIDはサーバでそれぞれのJavaScriptクライアントを識別するためのもの。クライアントIDはアプリケーションでどのように設計しても良い。

トークン

トークンはJavaScriptクライアントがチャネルに接続して待ち受けることを可能にする役割がある。

サーバはそれぞれのクライアントのためにクライアントIDと有効期限などの情報を使って トークンを作成する。

チャネル

チャネルはサーバーがクライアントIDで識別されるJavaScriptクライアントにアップデートを送るための片方向の通信路。

サーバはクライアントからPOSTによってアップデートを受信すると、該当するクライアントにチャネルを使ってメッセージを送る。

メッセージ

メッセージはクライアントからサーバにPOSTによって送られる。

受信すると、サーバはクライアントIDで識別されたクライアントにチャネルを使ってメッセージを渡す。

メッセージは32KBまで。

URLを送るときに生のメッセージで送るのは避ける。その代わりにメッセージが損なわれずに到達するようにJSONエンコーディングを使う。

ソケット

JavaScriptクライアントはサーバから提供されたトークンを使ってソケット開く。チャネルで更新を待ち受けるためにソケットを使う。

Channel APIを使うためのコード

チャネルを作る(サーバ)

channelServiceを取得して、チャネルを作成。トークンを生成する。

ChannelService channelService =
  ChannelServiceFactory.getChannelService();
String token = channelService.createChannel(clientId);

チャネルに接続する(クライアント)

サーバからもらったトークンを使ってチャネルを開く。

channel = new goog.appengine.Channel(token);
socket = channel.open();
socket.onopen = onOpened;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;

サーバーからクライアントへのメッセージ送信(サーバ)

channelService.sendMessage(
  new ChannelMessage(clientId, messageString));

トークンとセキュリティ

createChannel()によって返されるトークンは他に知られないように秘密にしておく。もしトークンが盗まれたらチャネルに送られたメッセージを待ち受けできてしまう。

トークンは2時間で期限切れになる。クライアントが2時間以上チャネルに接続したままでいると、ソケットのonerror(), onclose()がコールバックされる。

注意

クライアントID毎に1つのクライアント

1つのクライアントIDで一度にチャネルに接続できるのは1つのクライアントだけ。アプリケーションは1つのクライアントIDでメッセージを多数のクライアントにばら撒くことはできない。

ページ毎に1つのチャネル、チャネル毎に1つのクライアント

クライアントは1ページにつき1つのチャネルにだけ接続できる。

もしアプリケーションがクライアントに複数の種類のデータを送る必要があるならサーバサイドでデータを集めてクライアントのソケットのonmessageハンドラに送る。

クライアントの接続と切断の追跡

チャネルに接続したクライアントには"プレゼンス"の概念がない。これはクライアントのチャネルへの接続または切断についてアプリケーションに通知されないことを意味する。

もし、アプリケーションがクライアントの接続を追跡する必要があるなら、1つの方法としてクライアントの状態を通知するために適当な間隔でPOSTメッセージをサーバに送るよう設計された関数をソケットのonopenプロパティにセットする。

2011年2月10日木曜日

Slim3でDatastoreを使う - データ操作編

 

この記事はこちらへ移動しました。
Slim3でDatastoreを使う

 

キーの作成

アプリ指定の値で作成
Datastore.createKey(…);

値を自動割り当て
Datastore.allocateId(…);

親エンティティを指定して作成
Datastore.createKey(parentKey, Child.class, value);
Datastore.allocateId(parentKey, Clind.class);

データオブジェクトの新規追加

org.slim3.datastore.DataStore.put(object);

非同期での保存も可能。
DataStore.putAsync(object);

複数のオブジェクトの保存はコレクションか配列をputに渡すか、putに複数のパラメータを並べる

オブジェクトのロード

Datastore.get(Class, Key);

複数のオブジェクトの取り出しもできる。
DataStore.get(Class, コレクション or 配列);
Datastore.get(Class, key1, key2, …);

オブジェクトの更新

オブジェクトを更新するときは、ロード(get)、オブジェクトの値の変更、保存(put)の順で行う。

オブジェクトの削除

Datastore.delete(Key);

ロードや更新と同様、複数オブジェクトを同時に削除できる。
子孫エンティティごと削除するには

Datastore.deleteAll(Key);

クエリ

メタデータ

データクラスをEclipseで作成すると、自動的にメタデータが作成される。クエリの作成の際にこのメタデータを使う。

DataClassのメタデータのインスタンスは次のように取得できる。

DataClassMeta meta = DataClassMeta.get();

フィルタ

EntityQuery query = Datastore.query(meta);
List results = query.filter( meta.property1.equal(value1), meta.progerty2.greaterThan(value2), … ).asList();

インメモリでフィルタ。
endsWith や contains が使える。
query.filterInMemory(meta.property.endsWith("XYZ"))

ソート

query.sort(meta.property1.asc, meta.property2.desc)

インメモリでソート
query.sortInMemory(meta.property.asc)

offset, limit

query.offset(5).limit(10)

クエリの実行と結果

query.asList()
query.asIterator()
query.asSingle()
query.asKeyList()
query.asKeyIterator()

祖先クエリ

[TODO] 後で調べる
List<Child> list = Datastore.query(Child.class, ancestorKey).asList();

kind なしの祖先クエリ

[TODO] 後で調べる
List<Entity> list = Datastore.query(ancestorKey).asList();

クエリの制限

不等式フィルタはクエリ中で1つのプロパティだけにしか使用できない。
不等式で使われたプロパティは最初のソート項目に無ければならない(ソートを指定しなければならない)。

インデックス

フィルタで指定したプロパティを持っていないエンティティは、クエリに引っかからない。
インデックスされないプロパティはクエリに引っかからない。
Text、Blob、@Attribute(unindexed = true) アノテーションをつけたプロパティはインデックスされない。
同じプロパティ名で別の型の値を持つエンティティがあると、型でソートされたあと値でソートされる。
対応するインデックス定義が無いクエリは失敗する。

インデックスの定義

単純なクエリに対するインデックスは自動作成される。

  • フィルタとソート順を使用しないクエリ
  • 等式フィルタと祖先フィルタのみを使用するクエリ
  • 単一のプロパティに対する不等式フィルタのみを使用するクエリ
  • フィルタなしで、プロパティに昇順か降順のどちらかのソート順が設定されているクエリ
  • 等式フィルタをプロパティに使用して、不等式または範囲フィルタをキーに使用しているクエリ

インデックスはxmlで定義。
クエリに必要なインデックス定義は開発環境でクエリを実行したときに自動的に作成される。

手作業で定義するときは WEB-INF/datastore-indexes.xml に書く。

datastore-indexes.xml の autoGenerate が true になっている場合、WEB-INF/appengine-generated/datastore-indexes-auto.xml にインデックスが自動生成される。

クエリカーソル

カーソルとは、フェッチ処理後の次の位置を表す文字列。
S3QueryResultList を通じてカーソルを使用する。

S3QueryResultList results = query.limit(20).asQueryResultList();
results.getEncodedCursor();
results.getEncodedFilters();
results.getEncodedSorts();
results.hasNext();

query.encodedCursor(encodedCursor)
.encodedFilters(encodedFilters)
.encodedSorts(encodedSorts)
.limit(20).asQueryResultList();

カーソルの制限
  • in や != フィルタのクエリには使えない
  • カーソルは同じクエリにしか使えない。kind、フィルタ、フィルタ値、祖先フィルタ、ソートが同じであること。
  • インデックス設定を変更するとクエリは無効になる

 

トランザクション

Transaction tx = Datastore.beginTransaction();
DataClass data = Datastore.get(tx, Dataclass.class, key);
data.setProperty(value);
Datastore.put(tx, data);
tx.commit();

単一のトランザクションでできることは

  • 1つのグループ内の複数のエンティティを変更すること
  • グループに新しいエンティティを追加すること

1つのトランザクション内では1つのエンティティグループの操作だけができる
(エンティティのロードも含めて)。
楽観的並列処理なのでトランザクションが失敗したらアプリ側で何度かリトライする。
トランザクションの外側のトランザクション分離性はRead committedに近いがトランザクションはSerializableで実行される。
トランザクション内の祖先クエリやgetはトランザクション内での変更があってもトランザクション開始時点のスナップショットを返す。

バージョンによる楽観的ロック

トランザクションを超える更新(Web画面のためのGETリクエストの後、更新のためのPOSTリクエストでDatastoreにputするなど)を行う場合、データのバージョンにより競合を検出できる。

エンティティに @Attribute(version = true) を付けたプロパティを用意することで、書き込みためのgetの際にすでに他の書き込みによってバージョンが変更されていたら ConcurrentModificationException が投げられる。

Datastore.get(Transaction, Class, Key, version)

グローバルトランザクション

複数のエンティティグループにまたがったトランザクションを実行できる。

GlobalTransaction gtx = Datastore.beginGlobalTransaction();
gtx.get(…);
gtx.put(…);
gtx.commit();

同じエンティティグループに対してローカルトランザクションとグローバルトランザクションを混ぜて使ってはいけない。グローバルトランザクションはパフォーマンス的にはローカルトランザクションと大きな違いはないそうなので、グローバルトランザクションを使う可能性があればそのエンティティグループの操作には常にグローバルトランザクションを使うと良い。

Slim3でDatastoreを使う - データ定義編

 

この記事はこちらへ移動しました。
Slim3でDatastoreを使う

 

モデルの定義

データのクラスに@Modelアノテーションを付ける
永続化するプロパティには getter, setter が必要
永続化しないフィールドには@Attribute(persistent = false) アノテーションを付ける

フィールドの型

フィールドで使える型はDatastoreの基本型、基本型のコレクション、シリアライズ可能なオブジェクト
フィールドで使える基本型 → Core Value Types
シリアライズ可能なオブジェクトはBlob型として格納される(@Attribute(lob = true)を付ける)
500文字(byte[]は500バイト)を超えるフィールドには@Attribute(lob = true)を付ける
配列はコレクションとして扱われない(シリアライズしてBlobになる?)
インデックスに対応した型はデフォルトでインデックスが付く
インデックスを付けたくないフィールドには@Attribute(unindexed = true)を付ける

サポートするコレクション型

ArrayList, LinkedList, HashSet, LinkedHashSet, TreeSet, List (ArrayList), Set (HashSet), SortedSet (TreeSet)

()内はプロパティから返される実際のインスタンスの型

自動的なプロパティ値の更新

@Attribute(listener = ???) アノテーションで値を自動更新するリスナを指定できる

保存するたびに更新

@Attribute(listener = ModificationDate.class)
Date updatedAt;

最初の保存のときだけ更新

@Attribute(listener = CreationDate.class)
Date createdAt;

キー

モデルクラスに1つだけキーを持たなければならない
キーのフィールドには @Attribute(primaryKey = true) を付ける
キーは パス、kind、ID によって構成される
kind はクラスの単純名から付けられる
kindはクラスの @Model(kind = "...") アノテーションで変更可能
IDはアプリケーションで指定した文字列か、Datastoreが付けた数値(キーをnullのまま保存)
キーは Datastore.createKey(Class, String) で作成してキープロパティにセットする

エンティティグループ

エンティティの親子関係はキーを作成するときに親エンティティのIDを指定することで決定する
プロパティに他のエンティティの参照を持ってもエンティティグループに入れるわけではない
エンティティの親子関係はあとで変えることはできない
先祖エンティティが削除されても子孫エンティティは削除されない

リレーションシップ

片方向1対1関連

ModelRef型のプロパティを作って次のように初期化とgetterメソッドを作成。

@Model public class Address { … }

@Model public class Employee {
    private ModelRef<Address> addressRef = new ModelRef<Address>(Address.class);
    public ModelRef<Address> getAddressRef() { return addressRef; }
}

インスタンスを作成して関連付けして保存

Address address = new Address();
Employee employee = new Employee();
employee.getAddressRef().setModel(address);
Datastore.put(address, employee);

ロードするときは、

Employee employee = Datastore.get(Employee.class, employeeKey);
Address address = employee.getAddressRef().getModel();

参照先はレイジーロードされる。

双方向1対1関連

先ほどの関連に逆方向の参照を追加。逆方向はInverseModelRef型にする。逆方向の参照は永続化しない。Addressクラスに以下のコードを追加。

@Attribute(persistent = false)
private InverseModelRef<Employee, Address> employeeRef = new InverseModelRef<Employee, Address>(Employee.class, "addressRef", this);

public InverseModelRef<Employee, Address> getEmployeeRef() { return employeeRef;}

これでAddressからEmployeeへの参照ができる。

address.getEmployeeRef.getModel();

プロパティに関連をセットするには正方向側(Employee側)のプロパティにセットする。逆方向にはsetModel()メソッドが無い。

片方向多対1関連

多 → 1 の参照を持つ関連の場合、片方向1対1の場合と同じ。

双方向多対1関連

多 → 1への参照に加え、1 → 多 への参照を追加。1側から多側への参照は永続化しない。

@Attribute(persistent = false)
private InverseModelListRef<Employee, Department> employeeListRef = new InverseModelListRef<Employee, Department>(Employee.class, "departmentRef", this);

public InverseModelListRef<Employee, Department> getEmployeeListRef() {…}

1側から多側を取得する場合は次のようにする。

List<Employee> employeeList = department.getEmployeeListRef().getModelList();

双方向多対多関連

関連クラスを作って、1 ← 多(関連クラス) → 1 のようにする。

関連クラスに1側への参照プロパティをそれぞれ作る。

@Model
public class EmployeeProject {
    …
    private ModelRef<Employee> employeeRef = new ModelRef<Employee>(Employee.class);
    private ModelRef<Project> projectRef = new ModelRef<Project>(Project.class);

    public ModelRef<Employee> getEmployeeRef() {…}
    public ModelRef<Project> getProjectRef() {…}
    …
}

1側は"双方向多対1"の逆参照と同様。EmployeeProjectに対して永続化しない InverseModelListRef<EmployeeProject, 1側クラス> のプロパティとgetterを作る。

ロードするときは片側(たとえばProjectオブジェクト)から関連オブジェクトのリストを取得して、関連オブジェクトから反対側のオブジェクトを得る。

relObject = project.getEmployeeProjectRef().getModelList();
relObject.getEmployeeRef().getModel();

2011年2月9日水曜日

Google App Engine 目次

slim3

Android 目次

Flash 目次

Java 目次

Apache Ivy

Datastore 概要まとめ (Google App Engine)

Datastore Java API 概要」のまとめ

エンティティ

エンティティ単位でデータを操作
エンティティはプロパティを持つ
プロパティは別のエンティティへの参照を持つこともできる
トランザクションが使える
トランザクションで複数の操作ができる
エンティティにはスキーマがない
JDO, JPAでアクセスできる
低レベルAPIもある
エンティティにはキーがある
キーか、プロパティに適合するクエリでエンティティをフェッチできる
クエリの結果はプロパティ値で並び替えることができる
プロパティは複数の値を持つこともできる

クエリとインデックス

クエリは指定されたクラスに対してキーとプロパティに対するフィルタと並び替えを指定できる
すべてのクエリはインデックスを使用する
インデックスは設定ファイルで定義する

トランザクションとエンティティグループ

エンティティの作成、更新、削除のすべての操作はトランザクションで実行される
1つのトランザクションで複数のエンティティを変更できる
同じエンティティグループに属するエンティティは同時に変更できる
エンティティグループに参加するには、そのエンティティグループのエンティティの子になればいい
親を持たないエンティティはルートエンティティになる
トランザクションはオプティミスティック・コンカレンシで実行される
エンティティグループの変更中はそのグループへのほかの更新はすぐにエラーになる
リトライはアプリケーションが行う

SQLとの違い

join が使えない
複数プロパティでの不等式のフィルタリングやサブクエリが使えない
データが一貫したプロパティを持っている必要はない
クエリで取得できるのはキーまたはエンティティ全体だけ

2011年2月8日火曜日

Google App Engineのメンテナンスモード

 

書きかけメモ。書かれていることはすべて未検証。
コードはすべてJava。

稼働中にメンテナンスモードになったら

Google App Engineは時折、計画されたメンテナンスを行うことがある。もしアプリケーションが稼働中にメンテナンスに入ったらどうなるか。

Datastore

読み取り専用モードになる。メンテナンスの間、新規のデータ追加や更新はできない。書き込みに関するAPIを呼び出すと com.google.apphosting.api.ApiProxy.CapabilityDisabledException が投げられる。

コード例などはここに書いてある。
Gracefully Degrading During Scheduled Maintenance

Memcache

put も get も静かに失敗する。put しても何も書き込まれないし、get しても何も返ってこない。もしメンテナンス中の put や get 呼び出しで例外を出してほしいなら

memcacheService.setErrorHandler(new StrictErrorHandler());

としておくと、com.google.appengine.api.memcache.MemcacheServiceException が投げられる。

メンテナンスの予定を取得するAPI

CapabilityState から取れる。

CapabilitiesService cs =
  CapabilitiesServiceFactory.getCapabilitiesService();
CapabilityState state= cs.getStatus(Capability.DATASTORE);
Date メンテナンス日 = state.getScheduledDate();

現在の稼働状況は、

Capability capability = state.getCapability();
String 稼動状況 = capability.getName();

というコードで取れる。Capability から得られるものがよくわからないが getName() で返ってくる文字列はCapabilityStatus という列挙型に入っている値のString表現だと思う。

CapabilityStatusには DISABLED, ENABLED, SCHEDULED_MAINTENANCE, UNKNOWN の4つの値が定義されている。

メンテナンス予定をメールで受け取る

専用のGoogleグループがあってそこで通知されている。Googleグループにはメールでの通知機能もあるのでメンテナンス予定をメールで受け取ることができる。(たぶん…)
Google App Engine Downtime Notify

2011年2月5日土曜日

GalaxySの解像度をAPIと実寸で比較 (Android)

Anddroid端末の解像度を取得する方法。
下のコードで画面のいろいろな情報を取得できる。

DisplayMetrics mtr = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(mtr);

GalaxySの画面の実寸サイズは長辺86mm 800px, 短辺52mm 480pxなので、長辺234.46153846153846153846153846154 dpi, 短辺 236.27906976744186046511627906977 dpi。

GalaxyS上でAPIで得られる数値はXdpi 234.46153, Ydpi 236.27907 なので実寸と同じ。こういうのはあまり信用できない数値が返ってくることが多いけど、試してみたGalaxySでは正確な数値が得られるようだ。

参考ページ

IS03、色々な機種のDpi情報が欲しいので、こちらからはGalaxy Sの情報を晒します

2011年2月3日木曜日

コリャ英和!で訳してみた

JBoss Cacheのドキュメントをコリャ英和! で翻訳してみた例。

自動翻訳の後、単語のグループ化と翻訳候補の選択をしただけでここまで訳せる。まだおかしい部分はあるけど、原文も参照すれば英語が苦手でも意味はわかる程度の訳になっている。最後の文はちょっとあれだけど。

[日本語訳]

スタンドアローンの Java プログラムで使われるとき、される必要があるすべては、ユーザ API と Configuration 章で論じられるように、 CacheFactory と Configuration インスタンスあるいは XML ファイルを使ってキャッシュのインスタンスを作ることです。

同じテクニックは、アプリケーションサーバで稼働しているアプリケーションがアプリケーションサーバの配置機能に頼るよりむしろプログラム的にキャッシュを配置することを望むとき、使われることができます。 これの例は javax.servlet.ServletContextListener によってキャッシュを実装する webapp でしょう。

生成の後に、あなたは、スプリング、 JBoss 極小コンテナなどのようなIOCコンテナを使うことによって、あるいは、 JNDI にそれをバインドするか、あるいはただキャッシュへのスタティックな 参照を持つことによって、異なったアプリケーションコンポーネントの間であなたのキャッシュインスタンスを共有することができました。

もし、あなたのキャッシュを配置した後で、あなたは JMX でそれへの管理インタフェースを見せることを望んで、 JMX でプログラムの登録で項を見ます。

[原文]
http://docs.jboss.org/jbosscache/3.2.1.GA/userguide_en/html/deployment.html

When used in a standalone Java program, all that needs to be done is to instantiate the cache using the CacheFactory and a Configuration instance or an XML file, as discussed in the User API and Configuration chapters.

The same techniques can be used when an application running in an application server wishes to programatically deploy a cache rather than relying on an application server's deployment features. An example of this would be a webapp deploying a cache via a javax.servlet.ServletContextListener.

After creation, you could share your cache instance among different application components either by using an IOC container such as Spring, JBoss Microcontainer, etc., or by binding it to JNDI, or simply holding a static reference to the cache.

If, after deploying your cache you wish to expose a management interface to it in JMX, see the section on Programatic Registration in JMX.

2011年2月2日水曜日

これは欲しい、というAndroid搭載機いろいろ

なんかAndroid機もいろいろなバリエーション出てきたので、自分が欲しいと思ったのだけメモ。

Dell Streak 7

2/2発売(アメリカ) 7インチ 800x480 Android2.2 Tagra2 199.99ドル(2年契約)

GALAXY Player YP-GB1

2月中旬発売(韓国) 4インチ 800x480 Android2.2 A-GPS Gセンサー デジタルコンパス バッテリー1200mAh
16GBモデル 45万9000ウォン、8GBモデル 39万9000ウォン

COWON D3 plenue

2月発売 1080p再生可能 HDMI 3.7インチ 800x480AMOLED Android2.1
32GBモデル 37800円(3780ポイント還元)、16GBモデル32800円(3280ポイント還元) (2/1 sofmap.com)

Redfox WizPAD T1030iA

10インチ 1024x600 Atom N455 Android1.6
32GBモデル56,980円、16GBモデル47,800円 (1/29 Akiba PC Hotline)

MINIPAD S5PV210

8インチ 感圧式タッチパネル Android2.2 25800円 (1/29 Akiba PC Hotline)

購入した人のブログ

GALAXY Tab SC-01C

2010年11月26日発売 7インチ 1024x600 Android2.2