GAE対応の為にローカルファイルにアクセスしていた所を抽象化して JDOでも実装する事にした。

JDO(Java Data Objects)がそもそも何かって言うとJavaのオブジェクトを 永続化して保存/復元できる様にする仕組み、 EntityBean とか似たようなのは幾つか有ったはずだけど 結局どれもうまく行って無いから新しいのが出て来ちゃうんだろうね。

JDOを利用する手順は

  1. ${appengine.sdk.dir}/lib/user/orm/*.jar を war/WEB-INF/lib/ にコピーする。
  2. 保存したいクラスにアノテーションを付けて宣言する。
  3. PersistenceManager を使って保存/復元する。
となる。

保存用クラスにアノテーションを付ける。

  • class に @PersistenceCapable(...)
  • 保存対象のフィールドに @Persistent
  • 主キーのフィールドに @PrimaryKey
  • 主キーは必ず必要で long 又は String である事。
             :
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Blob;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class PageInfoJDO {

	@PrimaryKey
	@Persistent
	private String  pageName;

	@Persistent
	private long    lastModified;

	@Persistent
	private boolean isDirectory;

	@Persistent
	private long    length;

	@Persistent
	private Blob    body = null;

	// setter/getter は必要。

オブジェクトを保存する。

  • PersistenceManager.makePersistent() を呼ぶだけ。
  • トランザクションはオブジェクト1個だけの場合は本来必要無い。
  • PersistenceManagerFactoryは使い回せとドキュメントに書いて有った。
	public static void putPageInfo(PageInfoJDO info) {
		PersistenceManager pm = getPMF().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		tx.begin();
   		try {
			pm.makePersistent(info);
			tx.commit();
		} finally {
			if (tx.isActive()) tx.rollback();
			pm.close();
		}
	}
	public static PersistenceManagerFactory getPMF() {
		if (PMF == null) {
			PMF = JDOHelper.getPersistenceManagerFactory("transactions-optional");
		}
		return PMF;
	}

オブジェクトを取得する。

  • クエリを実行して復元オブジェクトを受け取る。
  • クエリの生成方法はいろいろ有るので PersistenceManager の apidoc を読むべし。
  • PersistenceManagerFactoryは使い回せとドキュメントに書いて有った。
	public static PageInfoJDO getPageInfo(String pageName, boolean withBody) {
		PersistenceManager pm = getPMF().getPersistenceManager();
		try {
			List<PageInfoJDO> sel = (List<PageInfoJDO>) pm.newQuery(PageInfoJDO.class, "pageName=='"+pageName+"'").execute();
			if (sel.size() == 0) return null;
			PageInfoJDO info = sel.get(0);
			if (withBody) info.getBytes();
			return info;
		} finally {
			pm.close();
		}
	}

と、結果だけを書いたが実はここまで来るのにかなりはまっている。

最初にはまった例外。

org.datanucleus.exceptions.ClassNotPersistableException: The class "kotemaru.wsjs.gae.PageInfoJDO" is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data/annotations for the class are not found.

どうみても PageInfoJDO の宣言が効いていない。 しかし、いくらドキュメントを読み直してもこれ以上の宣言も設定も必要無い。
で、デモの build.xml を調べてやっと分かった。
コンパイル後のクラスを再加工する必要が有ったのだ。

これがチュートリアルの手順にはどこにも書いて無い。 なぜかと言うと Eclips 環境だとかってにやってくれるから。 別のマニュアルにはチョロっと書いて有ったけどチュートリアルでも 一言ぐらい言及しろつーの、通常のJavaと手順が違うんだから。

次にはまったのがトランザクションなんだけど疲れたのでまた明日。