 | Nidhi Singh (nidhisin@in.ibm.com), Software Engineer, IBM India Software Labs Rohit Babbar (rohitba@in.ibm.com), Software Engineer, IBM India Software Labs
20 Nov 2007 Learn how the Dynamic Eclipse Modeling Framework (EMF) allows developers to build
dynamic Ecore-based models on demand without the need to generate Java™ implementation
classes. This article introduces the APIs, and shows how to serialize and load dynamic
Ecore models and their instances.
Eclipse Modeling Framework (EMF) describes data models and enables easy code generation
from different types of data model artifacts, such as XML Schema, the Rational
Rose® model, the Ecore model, or Java annotations. In the process of code generation, the EMF generator
creates model code, which includes type-safe interfaces and implementation classes for
the data model. However, in certain situations, these type-safe interfaces and
implementation classes are not required by the application. Instead, data objects are
required that could be shared among or processed further by application components.
In such situations, Dynamic EMF comes in handy because it allows application
developers to manufacture an in-memory core model at run time programmatically, make
instances of it dynamically, and access the model instance elements using the EMF reflective API.
Why Dynamic EMF?
The primary value of Dynamic EMF is that it allows you to build an Ecore-based model at
run time with just few lines of code, then create and access instances of this dynamic
model for a variety of purposes. Building a core model in this way helps avoid
generating the interfaces and implementation classes when they are not required.
This methodology of creating models and model instances is particularly useful in,
though not limited to, the scenarios where:
-
No type-safe interfaces or implementation classes are needed
—
Only simple data objects are required to be shared among application components. In
this case, generating model code using the EMF code generator would be overhead for the
application because it would have to unnecessarily maintain and deploy the entire set of
generated interfaces/classes. Using Dynamic EMF, a core model containing dynamic classes
can be programmatically created and instantiated on the fly. The instances of these
dynamic classes can then be used for sharing the data or for further processing by the
application components.
-
Data model is known at run time only
— In this scenario, since
the model of the data is not known at development time, creating static models through
the EMF code generator would not make a good option. Dynamic core models, which could
be built and instantiated at run time, would be better suited to the application
requirements in such cases.
Creating a dynamic in-memory core model
We start by building a dynamic Ecore-based model programmatically, then create dynamic
instances of the model. Later, we will see how to read and write the values of elements present in the model instance.
Creating dynamic Ecore-based model/metamodel
We will consider a bookstore model to demonstrate the creation of our
dynamic Ecore model. For clarity, we represent the model using the Unified Modeling
Language (UML).
Figure 1. BookStore model
We start by creating a set of core model elements, including an EcoreFactory instance,
an EcorePackage instance, two EClass instances, and an EPackage instance. See Listing 1.
Listing 1. Create core model elements
/*
* 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");
|
The EcoreFactory provides methods to create model elements
like EClass, EAttribute, EPackage, etc. Using the instance of EcoreFactory, we create two EClass
instances: one to represent the BookStore class and other to
represent the Book class (as specified in the BookStore model). Next, we create an EPackage, where our BookStore and Book classes would ultimately be placed. Then we define the
name and nsPrefix attributes of bookStoreEPackage. Since a package's name need not be unique, a URI
should be provided to bookStoreEPackage to uniquely identify
it. This is done by setting the value of the nsURI attribute
using the setNsURI() method.
Now we create attributes, as specified by the BookStore data
model, for our dynamic classes. To set modeled data types for each attribute, we
instantiate EcorePackage, which contains accessors for the
metaobjects to represent each data type. See Listing 2.
Listing 2. Create attributes of dynamic classes
/*
* 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());
|
Next, we need to add the attributes of each class to the eStructuralFeatures list of the respective class. And finally, we
will place both of our classes in our dynamic package, bookStoreEPackage. This completes the creation of our metamodel for
the given BookStore model.
Listing 3. Associate attributes with respective
classes and place classes in dynamic package
/*
* 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);
|
Creating dynamic model instances
Having created our in-memory Ecore model, we can create instances of
dynamic classes present in the model. We first obtain an EFactory instance by invoking the getEFactoryInstance() method on bookStoreEPackage.
Listing 4. Create dynamic instances
/*
* 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);
|
Had the implementation of this model been generated by the EMF code generator, it would
have provided implementation classes for the package and the factory of the model. The
generated package being initialized (via its constructor) registers the generated
factory and instantiates an eFactoryInstance reference to the
generated factory class. Hence, invoking the getEFactoryInstance() method on the generated package would return
the corresponding generated factory. Since we do not have any generated factory in our
dynamic core model, or generated classes of any type, calling the getEFactoryInstance() method on bookStoreEPackage returns an instance of dynamic factory EFactoryImpl.
The EFactoryImpl defines the create() operation, which creates and returns a new instance of the
model class specified as an argument. In the case of generated model code, this method
is overridden by the generated factory to create and return instances of corresponding
generated model classes. In the dynamic metamodel, since there are no generated
factory/model implementation classes, invoking the create()
method on the EFactory instance would return an instance of
EObjectImpl.
Reading and writing the dynamic model
The EObjectImpl class contains implementation for all
reflective APIs, which can be used to access attributes of our dynamic classes,
enabling you to read and write the model, as shown below.
Listing 5. Get/Set the values of model instance elements
/*
* 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);
|
On similar lines, other reflective APIs implemented in EObjectImpl class (eIsSet() and eUnSet()) can be invoked on the model classes when required.
Serializing
the dynamic model
The dynamic model can be serialized using four fundamental interfaces of the EMF
Persistence framework: Resource, ResourceSet, Resource.Factory, and URIConverter. The procedure of serialization will depend on whether
we want to serialize the model in the same program in which we would deserialize it, or
if we wish to serialize it in some other program independent of the program in which we
would load or deserialize the model.
Do the following if serialization and deserialization of the core model is to be done
in the same program; if not, move on to Listing 7. To initialize the process of
serialization, we first register XML resource factory to handle files with any
extension, as shown in Listing 6. Next, we create an empty resource in the resource set
by invoking the createResource() method on the ResourceSet instance and passing the actual location of the resource
as URI. We add our EObject (bookStoreEObject) to the contents list of this resource and using
the save() method, copy the resource to its persistence
storage. (URIConverter can be used, if needed, by the
resource set to locate the resource or to normalize an input URI into an actual URI of the resource.)
Listing 6. Serialize the dynamic model instance
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();
}
|
The resultant serialized instance of our dynamic model would look like Figure 2.
Figure 2. Serialized instance of dynamic mode
Use the following serialization procedure if serialization and deserialization of the
core model is to be done in different, independent programs; if not, go back to Listing
6. While loading the dynamic model, we need access to the dynamic package bookStoreEPackage. If the model is to be loaded in the same program
in which it is serialized, this can be easily done (see the next section).
But if the model is to be loaded in a program different from the one in which it is
serialized, we need to serialize our dynamic Ecore model before serializing the model
instance to be able to access bookStoreEPackage.
Listing 7. Serialize the metamodel
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();
}
|
We first register an XML resource factory implementation to handle files with an Ecore
extension because the core model would be serialized using this extension. Next, we
create an empty resource and add our dynamic package, bookStoreEPackage, to the contents
list of this newly created resource. Finally, we save this resource.
The resulting serialized dynamic core model, or the metamodel, appears as shown in Figure 3.
Figure 3. Serialized dynamic core model
Now we serialize the model instance document: bookStore.xml. The only difference is
that this time, the instance document is serialized with an additional attribute: xsi:schemaLocation. The loader will use this attribute to locate the persisted resource, bookStore.ecore, containing the serialized EPackage needed to load the model instance document.
Listing 8. Serialize model instance
document with xsi:schemaLocation attribute
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();
}
|
The serialized model instance document, bookstore.xml, would appear with the xsi:schemaLocation attribute, as shown below.
Figure 4. Serialized model instance with xsi:SchemaLocation attribute
Deserializing/Loading dynamic model
We will now see how to load the dynamic model instance document that we just serialized.
Use this deserialization procedure if serialization and deserialization of the core
model is done in the same program. As in the process of serialization, we first register XML
the resource factory implementation to handle files with any extension. Thereafter, we add
our dynamic bookStoreEPackage to the package registry using
the same namespace URI we had given while serializing the model. (This URI appeared as xmlns:book=http:///com.ibm.dynamic.example.bookstore.ecore in our
resultant serialized model instance document.)
Listing 9. Load the model instance document
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));
|
After our package is registered with the package registry, we load the resource by
invoking the getResource() method on the resource set
instance. This will load our model instance document using the URI we passed as an
argument to the getResource() method. Finally, we access the
serialized instances in the document using reflective APIs, as shown above.
Follow this procedure if serialization and deserialization of the core model is done in
different, independent programs. Here, the procedure of
loading the instance document remains the same, except that we do not have to add our
dynamic bookStoreEPackage to the package registry of the
ResourceSet. This is because when we load the instance
document, bookStore.xml, the loader would find the namespace URI to package URI mapping
in the xsi:schemaLocation attribute of the instance document.
Using this mapping, the loader would automatically load the serialized bookStore.ecore model containing the dynamic bookStoreEPackage. Once this dynamic package is loaded, the
serialized instances can be accessed in the usual manner using EMF-reflective APIs, as illustrated in Listing 9.
Limitations
Models built using Dynamic EMF are a bit slower and use more space compared to the
models generated through the EMF generator. This is because dynamic models rely on
dynamic implementation of the reflective API, provided by the EObjectImpl class, for getting and setting of dynamic features of an
instance. For example, the dynamic model would use a slower eGet(EstructuralFeature myFeature) method as opposed to a faster
(generated) getMyFeature() method used by the generated core
model. Furthermore, dynamic settings require additional space in dynamic model
instances, which is not needed if the model code is generated using the EMF code generator.
Conclusion
You learned how to build Ecore-based models using Dynamic EMF that can be created and
instantiated on the fly without having corresponding Java implementation classes. The
usage of this approach of building models becomes particularly interesting in scenarios
where part of the application model code is generated through the EMF code generator,
and the rest of the model code is built using Dynamic EMF. In this and similar
scenarios, Dynamic EMF, if leveraged effectively, can go a long way in enabling
applications to share data reflectively. At the same time, it can reduce the generated
implementation code that would otherwise need to be maintained as the model evolves.
Download | Description | Name | Size | Download method |
|---|
| Sample code | os-dynamicemf.sampleDynamicBookstoreModel.zip | 8KB | HTTP |
|---|
Resources Learn
-
The Eclipse Modeling Framework (EMF) is home
to all EMF-related documentation and downloads.
-
The EMF
User’s Guide explains concepts related to EMF and illustrates, with examples, the
process of creating and generating models from different types of artifacts.
-
Read the developerWorks series "Model with the Eclipse Modeling Framework" to get started with EMF. Part 1 discusses how to
generate code from your model; Part 2
discusses the Java Emitter Templates (JET); and
Part 3
discusses JMerge, which can be used to
customize the output of JET templates.
-
The IBM Redbooks titled "Eclipse Development using the
Graphical Editing Framework and the Eclipse Modeling Framework" forms an introductory
study for EMF that details the features of EMF with an elaborate example.
-
Check out the "Recommended Eclipse reading list."
-
Browse all the Eclipse content on developerWorks.
-
New to Eclipse? Read the developerWorks article "Get started with Eclipse Platform" to learn its origin and architecture, and how to extend Eclipse with plug-ins.
-
Expand your Eclipse skills by checking out IBM developerWorks' Eclipse project resources.
-
To listen to interesting interviews and discussions for software developers, check out check out developerWorks podcasts.
-
For an introduction to the Eclipse platform, see "Getting started with the Eclipse Platform."
-
Stay current with developerWorks' Technical events and webcasts.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
Get products and technologies
Discuss
-
EMF newsgroups
are the most useful resource for EMF questions. You can post comments to clarify any
queries and keep updated about new developments.
-
The Eclipse Platform newsgroups should be your first stop to discuss questions regarding Eclipse. (Selecting this will launch your default Usenet news reader application and open eclipse.platform.)
-
The Eclipse newsgroups has many resources for people interested in using and extending Eclipse.
-
Participate in developerWorks blogs and get involved in the developerWorks community.
About the authors  | 
|  | Nidhi Singh is a developer in the Autonomic Computing Technology Centre at IBM India. She has authored EMF-based editors while building Self-Managed Resource (SMR) run-time components. She has been with IBM for more than three years. Her interest areas include data structures, algorithms, and discrete mathematics. |
 | 
|  | Rohit Babbar is working as a software engineer as part of IBM's Host Access Client Package team under Rational software group. His primary interests include topics in theoretical computer science like graph theory and approximation algorithms. |
Rate this page
|  |