動的 EMF を使ってメタモデルを構築する

Java 実装クラスを生成せずにオンデマンドで動的 Ecore モデルを作成する方法

動的 EMF (Eclipse Modeling Framework) が、Java™ 実装クラスを生成しなくても開発者がオンデマンドで動的 Ecore モデルを構築できるようにする仕組みを学んでください。この記事では EMF の API を紹介し、動的 Ecore モデルとそのインスタンスをシリアライズしてロードする方法を説明します。

Nidhi Singh, Software Engineer, IBM India Software Labs

Nidhi Singh Nidhi Singh は、インド IBM Autonomic Computing Technology Centre の開発者です。彼女は SMR (Self-Managed Resource) ランタイム・コンポーネントのビルドに取り組む傍ら、EMF ベースのエディターを作成しました。IBM に入社してから 3 年以上経ちます。専門分野には、データ構造、アルゴリズム、離散数学が含まれます。



Rohit Babbar, Software Engineer, IBM India Software Labs

Rohit BabbarRohit Babbar は、Rational ソフトウェア・グループの IBM Host Access Client Package チームでソフトウェア・エンジニアとして働いています。専門としているのは、グラフ理論や近似アルゴリズムなどの理論コンピューター・サイエンス関連です。



2007年 11月 20日

データ・モデルを記述する EMF (Eclipse Modeling Framework) は、XML Schema、Rational Rose モデル、Ecore モデル、Java 注釈など、さまざまなタイプのデータ・モデル成果物から簡単にコードを生成できるフレームワークです。コード生成の過程で EMF ジェネレーターが作成するモデル・コードには、データ・モデルのタイプ・セーフなインターフェースと実装クラスが含まれます。ただし特定の状況では、このようなタイプ・セーフなインターフェースと実装クラスではなく、代わりにアプリケーション・コンポーネント間で共有したり、アプリケーション・コンポーネントによるさらなる処理を行ったりすることが可能なデータ・オブジェクトがアプリケーションに必要になる場合もあります。

そのような場合に役立つのが、動的 EMF です。なぜなら動的 EMF では、アプリケーション開発者が実行時にプログラムによってメモリー内にコア・モデルを構築してそのインスタンスを動的に作成し、EMF のリフレクティブ API を使ってモデル・インスタンス要素にアクセスできるからです。

動的 EMF を使用する理由とは

動的 EMF の最大の価値は、わずか数行のコードで実行時に Ecore モデルを構築し、この動的モデルのインスタンスをさまざまな目的で作成して使用できることです。このようにコア・モデルを構築すると、インターフェースと実装クラスが必要ないときには生成されないようにすることができます。

このようなモデルおよびモデル・インスタンスの作成方法が役立つシナリオとしては、以下の例が挙げられます。

  • タイプ・セーフなインターフェースまたは実装クラスが必要ない場合 ― 単純なデータ・オブジェクトのみをアプリケーション・コンポーネント間で共有する必要がある場合です。この場合、EMF コード・ジェネレーターを使ってモデル・コードを生成すると、生成されたすべてのインターフェース/クラスを不必要なものまで維持してデプロイしなければならなくなるので、アプリケーションのオーバーヘッドになります。動的 EMF を使用すれば、動的クラスが含まれるコア・モデルをプログラムによって作成し、その場でインスタンス化することが可能になります。このような動的クラスのインスタンスは、データを共有したり、アプリケーション・コンポーネントで追加処理を行う目的で使用することができます。
  • 実行時にならないとデータ・モデルが判明しない場合 ― 開発の段階ではデータ・モデルが不明というこのシナリオでは、EMF コード・ジェネレーターで静的モデルを作成するのは賢い方法と言えません。このような場合には、実行時に構築してインスタンス化できる動的コア・モデルのほうがアプリケーションの要件に適しています。

メモリー内での動的コア・モデルの作成

まず始めにプログラムによって動的 Ecore モデルを構築し、それからこのモデルの動的インスタンスを作成します。その後に、モデル・インスタンスに含まれる要素の値を読み取る方法、そして値を書き込む方法を説明します。

動的 Ecore モデル/メタモデルを作成する方法

この記事ではブックストアのモデルを例に、動的 Ecore モデルの作成方法を説明します。明確さを期して、このモデルの表現には UML (統一モデリング言語: Unified Modeling Language) を使用します。

図 1. BookStore モデル
BookStore モデル

最初に作成するのは、一連のコア・モデル要素です。これには EcoreFactory インスタンス、EcorePackage インスタンス、EClass インスタンス (2 つ)、そして EPackage インスタンスが含まれます (リスト 1 を参照)。

リスト 1. コア・モデル要素の作成
/*
* Instantiate EcoreFactory
*/
EcoreFactory theCoreFactory = EcoreFactory.eINSTANCE;

/*
* Create EClass instance to model BookStore class
*/
EClass bookStoreEClass = theCoreFactory.createEClass();
bookStoreEClass.setName("BookStore");

/*
* Create EClass instance to model Book class
*/
EClass bookEClass = theCoreFactory.createEClass();
bookEClass.setName("Book");

/*
* Instantiate EPackage and provide unique URI
* to identify this package
*/
EPackage bookStoreEPackage = theCoreFactory.createEPackage();
bookStoreEPackage.setName("BookStorePackage");
bookStoreEPackage.setNsPrefix("bookStore");
bookStoreEPackage.setNsURI("http:///com.ibm.dynamic.example.bookstore.ecore");

EcoreFactory は、EClassEAttributeEPackage などのモデル要素を作成するメソッドを提供します。この EcoreFactory のインスタンスを使用して、(BookStore モデルに指定された) BookStore クラスと Book クラスをそれぞれ表す 2 つの EClass インスタンスを作成します。次に BookStore クラスと Book クラスが最終的に配置されることになる EPackage を作成し、続いて bookStoreEPackage の名前および nsPrefix 属性を定義します。パッケージの名前は固有である必要はないので、URI を指定して bookStoreEPackage を一意に識別するようにしてください。URI を指定するには、setNsURI() メソッドを使って nsURI 属性の値を設定します。

今度は、動的クラスの属性を BookStore データ・モデルで指定されているように作成します。モデル化したデータ型を属性ごとに設定するには、各データ型を表すメタオブジェクトのアクセサーが含まれる EcorePackage をインスタンス化します (リスト 2 を参照)。

リスト 2. 動的クラスの属性作成
/*
* Instantiate EcorePackage
*/
EcorePackage theCorePackage = EcorePackage.eINSTANCE;

/*
* Create attributes for BookStore class as specified in the model
*/
EAttribute bookStoreOwner = theCoreFactory.createEAttribute();
bookStoreOwner.setName("owner");
bookStoreOwner.setEType(theCorePackage.getEString());
EAttribute bookStoreLocation = theCoreFactory.createEAttribute();
bookStoreLocation.setName("location");
bookStoreLocation.setEType(theCorePackage.getEString());
EReference bookStore_Books = theCoreFactory.createEReference();
bookStore_Books.setName("books");
bookStore_Books.setEType(bookEClass);
bookStore_Books.setUpperBound(EStructuralFeature.UNBOUNDED_MULTIPLICITY);
bookStore_Books.setContainment(true);

/*
* Create attributes for Book class as defined in the model
*/
EAttribute bookName = theCoreFactory.createEAttribute();
bookName.setName("name");
bookName.setEType(theCorePackage.getEString());
EAttribute bookISBN = theCoreFactory.createEAttribute();
bookISBN.setName("isbn");
bookISBN.setEType(theCorePackage.getEInt());

次に必要なのは、各クラスの属性をそれぞれのクラスの eStructuralFeatures リストに追加することです。そして最後に両方のクラスを動的パッケージ、bookStoreEPackag に配置すれば、この BookStore モデルのメタモデルは完成です。

リスト 3. 属性と対応するクラスとの関連付けと動的パッケージへのクラスの配置
/*
* Add owner, location and books attributes/references
* to BookStore class
*/
bookStoreEClass.getEStructuralFeatures().add(bookStoreOwner);
bookStoreEClass.getEStructuralFeatures().add(bookStoreLocation);
bookStoreEClass.getEStructuralFeatures().add(bookStore_Books);

/*
* Add name and isbn attributes to Book class
*/
bookEClass.getEStructuralFeatures().add(bookName);
bookEClass.getEStructuralFeatures().add(bookISBN);

/*
* Place BookStore and Book classes in bookStoreEPackage
*/
bookStoreEPackage.getEClassifiers().add(bookStoreEClass);
bookStoreEPackage.getEClassifiers().add(bookEClass);

動的モデル・インスタンスを作成する方法

メモリー内 Ecore モデルの作成が完了したら、次はモデルに含まれる動的クラスのインスタンスを作成する番です。まずは bookStoreEPackagegetEFactoryInstance() メソッドを呼び出して、EFactory インスタンスを取得します。

リスト 4. 動的インスタンスの作成
/*
* Obtain EFactory instance from BookStoreEPackage 
*/
EFactory bookFactoryInstance = bookStoreEPackage.getEFactoryInstance();

/*
* Create dynamic instance of BookStoreEClass and BookEClass
*/
EObject bookObject = bookFactoryInstance.create(bookEClass);
EObject bookStoreObject = bookFactoryInstance.create(bookStoreEClass);

このモデルの実装を EMF コード・ジェネレーターで生成したとすると、パッケージやモデル・ファクトリーの実装クラスが提供されることになります。生成されたパッケージを (そのコンストラクターを使って) 初期化すると、生成済みのファクトリーが登録され、そのファクトリー・クラスへの eFactoryInstance 参照がインスタンス化されます。したがって、生成されたパッケージで getEFactoryInstance() メソッドを呼び出すと、対応する生成済みのファクトリーが返されます。しかしこの例の場合は、動的コア・モデルには生成済みのファクトリーも生成済みのクラスも存在しないため、bookStoreEPackagegetEFactoryInstance() メソッドを呼び出すと動的ファクトリー EFactoryImpl のインスタンスが返されます。

EFactoryImpl が定義する create() 操作は、引数として指定されたモデル・クラスのインスタンスを新たに作成して返します。生成済みのモデル・コードの場合、この create() メソッドは生成済みのファクトリーによってオーバーライドされ、対応する生成済みモデル・クラスのインスタンスが作成されて返されます。一方、動的メタモデルの場合にはファクトリー/モデルの実装クラスは生成されないため、EFactory インスタンスで create() メソッドを呼び出すと EObjectImpl のインスタンスが返されることになります。

動的モデルの読み取り/書き込み方法

EObjectImpl クラスにはすべてのリフレクティブ API の実装が含まれます。これらの API を使用して動的クラスの属性にアクセスすれば、以下のようにモデルに対する読み取り/書き込み操作を行うことができます。

リスト 5. モデル・インスタンス要素の値の取得/設定
/*
* Set the values of bookStoreObject attributes
*/
bookStoreObject.eSet(bookStoreOwner, "David Brown");
bookStoreObject.eSet(bookStoreLocation, "Street#12, Top Town, NY");
((List) bookStoreObject.eGet(bookStore_Books)).add(bookObject);

/*
* Set the values of bookObject attributes
*/
bookObject.eSet(bookName, "Harry Potter and the Deathly Hallows");
bookObject.eSet(bookISBN, 157221);

/*
* Read/Get the values of bookStoreObject attributes
*/
String strOwner =(String)bookStoreObject.eGet(bookStoreOwner);
String strLocation =(String)bookStoreObject.eGet(bookStoreLocation);

/*
* Read/Get the values of bookObject attributes
*/
String strName =(String)bookObject.eGet(bookName);
Object iISBN = bookObject.eGet(bookISBN);

必要に応じて、上記と同じような行を使用して EObjectImpl クラスに実装された他のリフレクティブ API (eIsSet() および eUnSet()) をモデル・クラスで呼び出すこともできます。


動的モデルのシリアライズ

動的モデルは EMF パーシスタンス・フレームワークの 4 つの基本インターフェースである ResourceResourceSetResource.Factor、そして URIConverter を使ってシリアライズすることができます。シリアライズの手順は、モデルをシリアライズするプログラムとデシリアライズするプログラムを同じにするか、それともロードまたはデシリアライズするプログラムとは別のプログラムでシリアライズするかによって異なります。

コア・モデルのシリアライズとデシリアライズを同じプログラムで行う場合には以下の手順に従い、そうでない場合はリスト 7 に進んでください。シリアライズのプロセスを初期化するには、リスト 6 に示すように、まず拡張子に関わりなくファイルを処理するように XML リソース・ファクトリーを登録します。次に、リソース・セット内に空のリソースを作成するため、ResourceSet インスタンスで createResource() メソッドを呼び出してリソースの実際の場所を URI として渡します。このリソースのコンテンツ・リストに EObject (bookStoreEObject) を追加し、save() メソッドを使ってリソースをその永続ストレージにコピーします (必要な場合にはリソース・セットで URIConverter を使ってリソースを検索したり、入力 URI をリソースの実際の URI に正規化することができます)。

リスト 6. 動的モデル・インスタンスのシリアライズ
ResourceSet resourceSet = new ResourceSetImpl();
/*
* Register XML Factory implementation using DEFAULT_EXTENSION
*/
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
    "*", new  XMLResourceFactoryImpl());

/*
* Create empty resource with the given URI
*/
Resource resource = resourceSet.createResource(URI.createURI("./bookStore.xml"));

/*
* Add bookStoreObject to contents list of the resource 
*/
resource.getContents().add(bookStoreObject);

try{
    /*
    * Save the resource
    */
      resource.save(null);
   }catch (IOException e) {
      e.printStackTrace();
   }

シリアライズした動的モデルのインスタンスは図 2 のようになります。

図 2. シリアライズした動的モデルのインスタンス
シリアライズした動的モデルのインスタンス

以下に説明するのは、コア・モデルのシリアライズとデシリアライズをそれぞれ別のプログラムで行う場合の手順です (同じプログラムで行う場合はリスト 6 に戻ってください)。動的パッケージ bookStoreEPackage には、動的モデルをロードしている間にアクセスする必要があります。シリアライズするプログラムと同じプログラムにモデルをロードするのであれば、アクセスするのは簡単です (次のセクションを参照)。しかし、シリアライズするプログラムとは別のプログラムにモデルをロードする場合、モデル・インスタンスをシリアライズする前に動的 Ecore モデルをシリアライズしておかないと bookStoreEPackage にはアクセスすることができません。

リスト 7. メタモデルのシリアライズ
ResourceSet metaResourceSet = new ResourceSetImpl();

/*
* Register XML Factory implementation to handle .ecore files
*/
metaResourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
    "ecore", new  XMLResourceFactoryImpl());

/*
* Create empty resource with the given URI
*/
Resource metaResource = \
metaResourceSet.createResource(URI.createURI("./bookStore.ecore"));

/*
* Add bookStoreEPackage to contents list of the resource 
*/
metaResource.getContents().add(bookStoreEPackage);

try {
     /*
     * Save the resource
     */
     metaResource.save(null);
    } catch (IOException e) {
      e.printStackTrace();
   }

コア・モデルをシリアライズする際は Ecore 拡張子の付いたファイルを使用するため、最初にこの拡張子が付いたファイルを処理する XML リソース・ファクトリー実装を登録します。続いて空のリソースを作成し、新しく作成したこのリソースのコンテンツ・リストに動的パッケージ bookStoreEPackage を追加します。この作業が終わったら、このリソースを保存します。

シリアライズした動的コア・モデル (メタモデル) は図 3 のようになります。

図 3. シリアライズした動的コア・モデル
シリアライズした動的コア・モデル

今度は、モデル・インスタンス文書 bookStore.xml をシリアライズします。この場合の手順で唯一異なる点は、インスタンス文書のシリアライズには xsi:schemaLocation という追加属性が使用されるということだけです。ローダーはこの属性を使って、モデル・インスタンス文書をロードするために必要なシリアライズ済み EPackage が含まれる永続リソース、bookStore.ecore を検索します。

リスト 8. xsi:schemaLocation 属性を使用したモデル・インスタンス文書のシリアライズ
ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
    "*", new  XMLResourceFactoryImpl());

Resource resource = resourceSet.createResource(URI.createURI("./bookStore.xml"));
resource.getContents().add(bookStoreObject);

/*
* Save the resource using OPTION_SCHEMA_LOCATION save option toproduce 
* xsi:schemaLocation attribute in the document
*/
Map options = new HashMap();
options.put(XMLResource.OPTION_SCHEMA_LOCATION, Boolean.TRUE);
try{
     resource.save(options);
   }catch (IOException e) {
     e.printStackTrace();
   }

上記のコードによってシリアライズされるモデル・インスタンス文書 bookstore.xmlには、以下のように xsi:schemaLocation 属性が指定されます。

図 4. xsi:schemaLocation 属性が指定されたシリアライズ後のモデル・インスタンス
xsi:schemaLocation 属性が指定されたシリアライズ後のモデル・インスタンス

動的モデルのデシリアライズ/ロード

このセクションでは、上記の手順でシリアライズした動的モデル・インスタンス文書をロードする方法を説明します。

コア・モデルのシリアライズとデシリアライズを同じプログラムで行う場合は、以下のデシリアライズ手順に従ってください。シリアライズのプロセスでの場合と同じく、最初に拡張子とは関わりなくファイルを処理するように XML リソース・ファクトリー実装を登録します。その上で、モデルのシリアライズで指定した名前空間 URI を使って動的 bookStoreEPackage をパッケージ・レジストリーに追加します (この URI は、最終的にシリアライズされたモデル・インスタンス文書内に xmlns:book=http:///com.ibm.dynamic.example.bookstore.ecore として表示されます)。

リスト 9. モデル・インスタンス文書のロード
ResourceSet load_resourceSet = new ResourceSetImpl();

/*
* Register XML Factory implementation using DEFAULT_EXTENSION
*/
load_resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
    "*", new XMLResourceFactoryImpl());

/*
* Add bookStoreEPackage to package registry
*/
load_resourceSet.getPackageRegistry().put(
    "http:///com.ibm.dynamic.example.bookstore.ecore",
     bookStoreEPackage);

/*
* Load the resource using the URI
*/
Resource load_resource = load_resourceSet.getResource(
    URI.createURI("./bookStore.xml"),true);

/*
* Read back the serialized instances of dynamic classes stored in the 
* resource
*/
EObject readBookStoreObject = (EObject)
    load_resource.getContents().get(0);
EObject readBookObject = 
    (EObject)((EList)(readBookStoreObject.eGet(bookStore_Books))).get(0);

System.out.println("Owner: " + readBookStoreObject.eGet(bookStoreOwner)
    + "\nLocation: " + readBookStoreObject.eGet(bookStoreLocation)
    + "\nBook:\n name: " + readBookObject.eGet(bookName) 
    + "\t isbn: " + readBookObject.eGet(bookISBN));

パッケージ・レジストリーにパッケージが登録されたら、リソース・セット・インスタンスの getResource() メソッドを呼び出してリソースをロードします。この場合、getResource() メソッドに引数として渡した URI を使ってモデル・インスタンス文書がロードされます。そして最後に、上記に示すようにリフレクティブ API を使用して文書内のシリアライズされたインスタンスにアクセスします。

次に説明するのは、コア・モデルのシリアライズとデシリアライズを別のプログラムで行う場合の手順です。この場合、インスタンス文書のロード手順は上記と同じですが、動的 bookStoreEPackageResourceSet のパッケージ・レジストリーに追加する必要はありません。その理由は、インスタンス文書 bookStore.xml をロードすると、ローダーがインスタンス文書の xsi:schemaLocation 属性に指定された名前空間 URI とパッケージ URI のマッピングを検出するためです。ローダーはこのマッピングを使用して自動的に、動的 bookStoreEPackage が含まれるシリアライズされた bookStore.ecore モデルをロードします。この動的パッケージがロードされると、リスト 9 に示すように EMF のリフレクティブ API を使って通常の方法でシリアライズされたインスタンスにアクセスできるようになります。


制約事項

動的 EMF で構築したモデルは EMF ジェネレーターで生成されたモデルと比べ、多少処理に時間がかかり、使用するスペースも増えます。これは、動的モデルが EObjectImpl クラスによって提供されるリフレクティブ API の動的実装に依存してインスタンスの動的機能を取得および設定するからです。例えば、生成されたコア・モデルは処理時間のかからない (生成された) getMyFeature() メソッドを使用するのに対し、動的モデルは処理時間のかかる eGet(EstructuralFeature myFeature) メソッドを使用します。さらに、動的設定には動的モデル・インスタンス内に追加スペースが必要になりますが、EMF コード・ジェネレーターでモデル・コードを生成する場合にはこのような追加スペースは必要ありません。


まとめ

この記事では、動的 EMF を使用して Ecore モデルを構築する方法を説明しました。この方法で構築された Ecore モデルは、対応する Java 実装クラスがなくてもその場でインスタンス化することができます。このモデル構築方法を使用する上で特に興味深いシナリオは、アプリケーションのモデル・コードの一部を EMF コード・ジェネレーターで生成し、残りのモデル・コードは動的 EMF で作成するというものです。この類のシナリオで動的 EMF を効果的に利用すれば、状況に合わせてアプリケーション間でデータを共有させることができると同時に、モデルが進化するにつれて生成される実装コードも減るため、管理しなければならないコードも減ることになります。


ダウンロード

内容ファイル名サイズ
Sample codeos-dynamicemf.sampleDynamicBookstoreModel.zip8KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

  • Eclipse に関する質問を投じる最初の場所として、Eclipse Platform newsgroups があります (このリンクをクリックすると、デフォルト Usenet ニュース・リーダー・アプリケーションが起動され、eclipse.platform が開きます)。
  • Eclipse newsgroups には Eclipse を利用し、拡張することに関心を持つ人達のために、さまざまなリソースが用意されています。
  • developerWorks blogs から developerWorks コミュニティーに加わってください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=277563
ArticleTitle=動的 EMF を使ってメタモデルを構築する
publish-date=11202007