2010年3月31日水曜日

EMF Transactionについてまとめてみました

EMFTransaction
EMF Transactionについて

EMFのデータモデルをトランザクションを用いて、複数スレッドからのアクセスコントロールやデータの整合性(atomic)を守る仕組み。 EditingDomainを用いてやり取りを行います。

簡単な説明はざっくり飛ばします。参考サイトを読んでください。


予備知識:EditingDomain

editingDomainは相関のあるEMFモデルを含んでいるのとコマンドを管理するものです。モデルはリソースセットの形式で保持されていています。
コマンドとモデルの編集はドメインを経由して、コマンドスタックを使って行います。
EditingDomainの補足機能として、基本的なコマンドのオーバーライドが使えます(OverrideableCommand. を参照)
ドメインはgetParent,getChildrenメソッドの結果でモデルへ階層的に役割を課す。
これはネストしたオブジェクトの削除などに使うRemoveCommandなどの実装に便利である。
また、ネストしたコピーなどに使うCopyCommandも便利である。

クラス図的にはこんな感じ

Transactional Commands API




Transactional Commands API


EditingDomain取得方法

EMFTransactionはEditingDomainを利用して、データのやり取りを行うため、EditingDomainを取得する必要があります。取得方法としてはTransactionUtilのユーティリティクラスのgetEditingDomainメソッドを利用します。
引数はEObject,Resource,ResourceSetのいずれかを入れてやれば取得が行えます。

データアクセス方法
データ取得

データの取得に関してはデータの変更が行われないため、取得方法はどのようにしても構いません。リソースからモデルを取得して任意のデータを取得することが可能です。

データ更新

コマンドを用いてデータ更新は行います。リソースから直接取得してデータ更新を行うとExceptionが投げられます。


public void setModuleDet(String moduleName, boolean detInfo) {
EObject owner = null;
EAttribute feature = null;


try{
Command cmd = editingDomain.createCommand(
SetCommand.class,
new CommandParameter(
owner,
feature,
detInfo
));

editingDomain.getCommandStack().execute(cmd);
} catch (Exception e) {
logger.warn(e.getMessage());
}
}



ロールバック

トランザクション環境で、データ整合性が失われた場合ロールバックするために、R/Wのトランザクションが発生する。しかし、基本的なコマンドスタックAPIはコマンドを失敗することはないので、
ロールバックしたときのフィードバックはない。
もし、トランザクションが始まっていなく、トランザクションがロールバックや、割り込み例外が発生したとき、トランザクションコマンドスタックはロールバック例外を投げることができる。


Library richmond = getLibrary("Richmond Branch"); // this would use a read-only transaction
TransactionalEditingDomain domain = getEditingDomain();

TransactionalCommandStack tstack = (TransactionalCommandStack) domain.getCommandStack();

Command cmd = domain.createCommand(
SetCommand.class,
new CommandParameter(richmond, EXTLibraryPackage.Literals.LIBRARY__ADDRESS, "5600 Perth St."));

try {
tstack.execute(cmd, Collections.EMPTY_MAP);
} catch (InterruptedException e) {
MessageDialog.openError(shell, "Command Failed",
"Interrupted while waiting for a read/write transaction.");
} catch (RollbackException e) {
ErrorDialog.openError(shell, "Command Failed", "Transaction rolled back",
e.getStatus());
}



コマンドオプション

コマンド実行に対して、色々オプションを付けて実行が行える。

Transaction Options




* OPTION_NO_NOTIFICATIONS:  リスナに変更通知を送らない。
* OPTION_NO_TRIGGERS: トリガーを使わない。
* OPTION_NO_VALIDATION: 変更に整合性チェックを行わない。
* OPTION_NO_UNDO: UNDO/REDOやロールバックのための変更記録を行わない。
* OPTION_UNPROTECTED: OPTION_NO_UNDO, OPTION_NO_VALIDATION, and OPTION_NO_TRIGGERSの複合ケース



TransactionalCommandStack stack;
Library library;

// don't tell the UI that we are changing the library name
stack.execute(
SetCommand.create(domain, library,
EXTLibraryPackage.Literals.LIBRARY__NAME, "Secret Name"),
Collections.singletonMap(
Transaction.OPTION_NO_NOTIFICATIONS, Boolean.TRUE));



トリガー
トリガーの実装も可能である。
Trigger API






// trigger ensuring that all libraries have names
class MyListener extends ResourceSetListenerImpl {
MyListener() { // only interested in changes to Library objects
super(NotificationFilter.createNotifierTypeFilter(
EXTLibraryPackage.Literals.LIBRARY));
}

public Command transactionAboutToCommit(ResourceSetChangeEvent event)
throws RollbackException {

List commands = new ArrayList();
Iterator iter = event.getNotifications().iterator();

while (iter.hasNext()) {
Notification next = (Notification) iter.next();
Library library = (Library) next.getNotifier();
if (library.getName() == null)
commands.add(SetCommand.create(
event.getEditingDomain(), library,
EXTLibraryPackage.Literals.LIBRARY__NAME, "A library"));
}

return commands.isEmpty()? null : new CompoundCommand(commands);
}
}



class MyTriggerListener extends TriggerListener {
MyListener() { // only interested in changes to Library objects
super(NotificationFilter.createNotifierTypeFilter(
EXTLibraryPackage.Literals.LIBRARY));
}

protected Command trigger(TransactionalEditingDomain domain,
Notification notification) throws RollbackException {

Library library = (Library) next.getNotifier();
if (library.getName() == null) {
return SetCommand.create(domain, library,
EXTLibraryPackage.Literals.LIBRARY__NAME, "A library");
}

return null;
}

}



参考サイト

0 件のコメント:

コメントを投稿