SourceForge.net Logo

openMDX Quick Start Guide

Version 1.16.4 DRAFT



www.openmdx.org





List of Figures

Figure 1: openMDX MDA Development Process 7

Figure 2: A sample MOF-compliant class diagram 8

Figure 3: JMI and XMI mapping of model and implementation of application plugin 15

Figure 4: A platform-independent provider consists of 1 or more plugins 16

Figure 5: Deployment of a platform-independent provider to a platform and adding the platform-specific deployment descriptor 20

Figure 6: The HelloWorld Example 22

Figure 7: Deployment of the HelloWorld example 22

Figure 8: Eclipse Project openMDX Java2 Enterprise (jre-1.5) 24

Figure 9: Eclipse Project openMDX Java2 Enterprise (jre-1.5) 25

Figure 10: Eclipse Project openMDX Java2 Enterprise (jre-1.5) 25

Figure 11: Eclipse Project openMDX Java2 Enterprise (jre-1.5) 26

Figure 12: Eclipse Project openMDX Java2 Enterprise (jre-1.5) 26

Figure 13: Eclipse Project openMDX Java2 Extension (jre-1.5) 27

Figure 14: Eclipse Project openMDX Java2 Extension (jre-1.5) 28

Figure 15: Eclipse Project openMDX Java2 Extension (jre-1.5) 28

Figure 16: Eclipse Project openMDX Java2 Extension (jre-1.5) 29

Figure 17: Eclipse Project openMDX Java2 Extension (jre-1.5) 29

Figure 18: Eclipse Project openMDX Core (jre-1.5) 30

Figure 19: Eclipse Project openMDX Core (jre-1.5) 31

Figure 20: Eclipse Project openMDX Core (jre-1.5) 31

Figure 21: Eclipse Project openMDX Core (jre-1.5) 32

Figure 22: Eclipse Project openMDX Core (jre-1.5) 32

Figure 23: Eclipse Project openMDX Core (jre-1.5) 33

Figure 24: Eclipse Project openMDX Core (jre-1.5) 33

Figure 25: Eclipse Project openMDX Example HelloWorld (jre-1.5) 34

Figure 26: Eclipse Project openMDX Example HelloWorld (jre-1.5) 35

Figure 27: Eclipse Project openMDX Example HelloWorld (jre-1.5) 35

Figure 28: Eclipse Project openMDX Example HelloWorld (jre-1.5) 36

Figure 29: Eclipse Project openMDX Example HelloWorld (jre-1.5) 36

Figure 30: Eclipse Project openMDX Example HelloWorld (jre-1.5) 37

Figure 31: Eclipse Project openMDX Example HelloWorld (jre-1.5) 38

Figure 32: Run Command-Line Client 39

Figure 33: Run Command-Line Client 40

Figure 34: Run Command-Line Client 41

Figure 35: Run Command-Line Client 42

Figure 36: Run HTML Servlet 43

Figure 37: Run HTML Servlet 44

Figure 38: Run HTML Servlet 45

Figure 39: Run HTML Servlet 46

Figure 40: Run HTML Servlet 47

Figure 41: Run HTML Servlet 47

Figure 42: Run HTML Servlet 48

Figure 43: Run HTML Servlet 48

Figure 44: Run HTML Servlet 49

Figure 45: Run HTML Servlet 49

Figure 46: Run HTML Servlet 50

Figure 47: Extending the Model - sayGoodBye() 51

Figure 48: Extending the Model - sayGoodBye() 52

Figure 49: Extending the Model - sayGoodBye() 53

Figure 50: Extending the Model - sayGoodBye() 54

Figure 51: Extending the Model - sayGoodBye() 55

Figure 52: Extending the Model - sayGoodBye() 56

Figure 53: Extending the Model - sayGoodBye() 57

Figure 54: Extending the Model - sayGoodBye() 58

Figure 55: Extending the Model - sayGoodBye() 59

Figure 56: Extending the Model - sayGoodBye() 60

Figure 57: Extending the Model - sayGoodBye() 61

Figure 58: Extending the Model - sayGoodBye() 62

Figure 59: Extending the Model - sayGoodBye() 63

Figure 60: Extending the Model - sayGoodBye() 64

Figure 61: Extending the Model - sayGoodBye() 65

Figure 62: Extending the Model - hitCount 66

Figure 63: Extending the Model - hitCount 67



List of Listings

Listing 1: JMI Interface of class Person 9

Listing 2: Implementation of the class Person 16

Listing 3: Tomcat users 50

Listing 4: Call sayGoodBye() 64

Listing 5: Error when running command-line client 65

Listing 6: Implementation of operation sayGoodBye() 65

Listing 7: Create a HelloWorld object with hitCount set to 0 68

Listing 8: Increment hitCount in method sayHello() 68

Listing 9: Increment hitCount in method sayGoodBye() 68

Listing 10: Client output after adding operation sayGoodBye() and attribute hitCount 69

Listing 11: Configuring the plugins of a provider 71

Listing 12: Configuring the JMI implementation classes 71



1 About this book

openMDX is an advanced implementation of the OMG Model Driven Architecture (MDA) initiative (also see http://www.omg.org/mda/). openMDX is an open, industrial-strength, model-driven runtime engine and framework for platform independent models (PIMs). Unlike most commercial tools, openMDX does not implement the generative PIM-to-PSM-mapping approach. Instead, openMDX provides a generic, distributed object engine which serves as a PIM platform. Business logic (derived and behavioral model elements) are added as plug-ins.

openMDX implements the OMG MDA standards MOF, UML, XMI, JMI and inherits many concepts and patterns from other standards, e.g. from Java Data Objects (JDO). An early version of the framework was used for the implementation of a MOF-compliant CORBA Interface Repository which was presented at the OMG meeting 1999 in Philadelphia (see [37], [38]). Since then, the framework has been extended and refined in all aspects. Today, the framework is powerful enough to run real business applications. openMDX allows the development of platform-independent, distributed component-based, model-driven applications.

1.1 Who this book is for

This document is a quick tour and introduction to openMDX. It explains how an openMDX based application is developed.

1.2 What do you need to understand this book

This book is a quick tour of how an openMDX based application is developed. To understand this book, you should be familiar with the most important MDA concepts. You do not need to have written openMDX applications before.

1.3 Tips, Warnings, etc.

We make use the following pictrograms:

Information provided as a "Tip" might be helpful for various reasons: time savings, risk reduction, etc.

You should carefully read information marked with "Important". Ignoring such information is typically not a good idea.

Warnings should not be ignored (risk of data loss, etc.)



2 Development Process

Basically, MDA based solutions can be classified as follows:

  1. Solutions which use models to drive the development process. This approach uses models - including platform independent models (PIM) and platform specific models (PSM) - to drive a code generation process. The generated code is then typically completed manually by business-logic. The code is then built and deployed.

  2. Solutions which use models to drive the runtime behavior of operational systems. A generic model execution runtime allows to execute the models. In this case no code generation is required. Every aspect of the application is expressed in the model.

openMDX follows the approach of solution II. However, openMDX allows to add behavior to the system using standard programming languages such as Java. However, this requires that a programmer has access to a 'Java binding' in order to be able to get access to the framework. For this, openMDX maps MOF compliant application models to language-specific (not platform-specific!) bindings. E.g. the JMI mapping allows to map MOF compliant models to Java interfaces, the XMI mapping allows to map models to XML files. These mappings allow a programmer to add business-logic to the system. A programmer can either implement a generated JMI interface or write a client against a JMI API. Business-logic can be added either manually or by (re-)using generic, model-driven programs and plugins. Samples of such generic, model-driven algorithms are:

  • Persistence. The openMDX persistence plugin implements an object-to-relational mapping in a generic, model-driven way.

  • GUI. The openMDX ObjectInspectorServlet is a generic, model-driven HTML rendering engine. The rendering process can be customized so that user-friendly interfaces can be created without programming a single line of code.

  • Audit, Security, etc. are functions which should typically be implemented in all parts of an application. With openMDX these functions can be implemented as generic, model-driven algorithms and added to the application as plugins.

Having a library of generic, model-driven algorithms at your disposal typically accelerates the software development process dramatically.

openMDX does not cover the full development process. While phases such as requirements engineering, testing, etc. are essential for the development of an application, they are not specific to openMDX and do not have tool support. They are therefore not described here.

Figure 1 shows the application development process with openMDX:

Figure 1: openMDX MDA Development Process

The task of each phase is in more detail as follows:

  • Modeling: Creation of the platform independent model (PIM). In the current version, openMDX supports MOF compliant class diagrams. Other diagram types which allow to model systems behavior are not supported yet. The support of UML action semantics, activity and state diagrams is an option which can be added at a later time to openMDX. The MOF mappings, e.g. JMI and XMI are then applied to these models. As defined by the MOF mappings, the class diagrams represent the specification of the external behavior of a component, i.e. the component's interface.

  • Development: In this step the developer writes clients and server code. It is important to notice that the code written is platform independent as we will see later.

  • Deployment: The models, the generated MOF mappings, the manually written code, the deployment information and the openMDX runtime are put together in the deployment phase to a runnable component and deployed to the runtime platform.

These activities are described in more detail in the following sections.

2.1 Modeling Phase

The PIM is created in the modeling phase. As already mentioned, the PIM defines the contract between the component and its clients. The following figure shows the MOF compliant class diagram of a simple Person/Address application:

Figure 2: A sample MOF-compliant class diagram

openMDX requires that the class diagrams are MOF compliant, i.e. use MOF model elements only. The restriction to MOF model elements has the advantage that it is possible to apply standard MOF mappings (e.g. JMI, XMI) to models and not just meta models. On the other side, the restriction to MOF model elements does not have a big impact on the modeler. Features such as association classes and class dependencies are typically not required for application-level modeling and if so, these features can be modeled in alternate ways. One exception are qualifiers: in addition to the MOF model elements, openMDX supports UML qualifiers. Qualifiers are required to specify in a precise way the object access paths. More information can be found in the openMDX tutorials.

Below is a sample when applying the JMI mapping to the class 'Person' from the model shown in Figure 2.

NOTE: All modeled classes inherit from the class 'BasicObject' (not shown). This is because the class Person extends the class BasicObject in the MOF model (which is not shown).

Listing 1: JMI Interface of class Person

package org.openmdx.test.app1.cci;

import javax.jmi.reflect.*;

import java.util.*;
import java.math.*;

public interface Person
extends org.openmdx.base.cci.BasicObject {

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGetSparseArray.vm
// ----------------------------------------------------------------------------
/**
* Retrieves a SparseArray containing all the elements for the attribute
* <code>additionalInfo</code>.
* @return A SparseArray containing all elements for this attribute.
* @exception JmiException If the values cannot be retrieved for some reason.
* @see #setAdditionalInfo(SparseArray)
*/
public Collection getAdditionalInfo(
);

/**
* Retrieves the element at the specified position in the SparseArray of
* all the values for the attribute <code>additionalInfo</code>.
* @param index The index of the element to return.
* @return The element at the specified position in the SparseArray of all values for this attribute.
* @exception JmiException If the value cannot be retrieved for some reason.
* @see #setAdditionalInfo(int,String)
*/
public String getAdditionalInfo(
int index
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeSetSparseArray.vm
// ----------------------------------------------------------------------------
/**
* Sets a new SparseArray containing all the new elements for the
* attribute <code>additionalInfo</code>.
* @param newValue A SparseArray containing all the new elements for this attribute.
* @exception JmiException If the values cannot be set for some reason.
* @see #getAdditionalInfo
*/
public void setAdditionalInfo(
Collection newValue
);

/**
* Sets a new array containing all the new elements for the attribute <code>additionalInfo</code>.
* @param newValue An array containing all the new elements for this attribute.
* @exception JmiException If the values cannot be set for some reason.
*/
public void setAdditionalInfo(
String[] newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGet1_1.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>age</code>.
* @return The non-null value for attribute <code>age</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
*/
public short getAge(
);

// ----------------------------------------------------------------------------
// Instance/IntfOperation.vm
// ----------------------------------------------------------------------------

public org.openmdx.generic.accessor.jmi.Void assignAddress(
org.openmdx.test.app1.accessor.jmi.PersonAssignAddressParams params
) throws RefException ;

/**
*/
public org.openmdx.generic.accessor.jmi.Void assignAddress(
List address
) throws RefException ;

// ----------------------------------------------------------------------------
// Instance/IntfReferenceGet0_1WithQualifier.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the reference <code>assignedAddress</code> for
* the specified qualifier attribute value.
* @param index The value for the qualifier attribute that qualifies this reference.
* @return The possibly null value for this reference.
* @exception JmiException If the value cannot be retrieved for some reason.
*/
public org.openmdx.test.app1.accessor.jmi.Address getAssignedAddress(
int index
);

// ----------------------------------------------------------------------------
// Instance/IntfReferenceGet0_nNoQualifier.vm
// ----------------------------------------------------------------------------
/**
* Retrieves a collection containing all the elements for the reference
* <code>assignedAddress</code>.
* @return A collection containing all the elements for this reference.
* @exception JmiException If the values cannot be retrieved for some reason.
*/
public Collection getAssignedAddress(
);

// ----------------------------------------------------------------------------
// Instance/IntfReferenceAddWithQualifier.vm
// ----------------------------------------------------------------------------
/**
* Appends the specified element to the list of all the values for the
* reference <code>assignedAddress</code> for a specified qualifier
* attribute value.
* @param index The qualifier attribute value that qualifies the reference to get the element
* to be appended.
* @param newValue The element to be appended.
* @exception JmiException If the value cannot be appended for some reason.
*/
public void addAssignedAddress (
int index,
org.openmdx.test.app1.cci.Address newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfReferenceAddWithoutQualifier.vm
// ----------------------------------------------------------------------------
/**
* Appends the specified element to the list of all the values for the
* reference <code>assignedAddress</code>.
* @param newValue The element to be appended.
* @exception JmiException If the value cannot be appended for some reason.
*/
public void addAssignedAddress (
org.openmdx.test.app1.cci.Address newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfReferenceRemoveWithQualifier.vm
// ----------------------------------------------------------------------------
/**
* Removes the qualified (by means of the specified qualifier attribute
* value) element from the list of all the values for the reference
* <code>assignedAddress</code>.
* @param index The qualifier attribute value that qualifies the reference
* to get the element to be removed.
* @exception JmiException If the value cannot be removed for some reason.
*/
public void removeAssignedAddress (
int index
);

// ----------------------------------------------------------------------------
// Instance/IntfReferenceRemoveWithoutQualifier.vm
// ----------------------------------------------------------------------------
/**
* Removes the specified element from the list of all the values for the
* reference <code>assignedAddress</code>.
* @param value The element to be removed.
* @exception JmiException If the value cannot be removed for some reason.
*/
public void removeAssignedAddress (
org.openmdx.test.app1.cci.Address value
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGet1_1.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>birthdate</code>.
* @return The non-null value for attribute <code>birthdate</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
* @see #setBirthdate(String)
*/
public String getBirthdate(
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeSet1_1.vm
// ----------------------------------------------------------------------------
/**
* Sets a new value for the attribute <code>birthdate</code>.
* @param newValue The non-null new value for attribute <code>birthdate</code>.
* @exception JmiException If the value cannot be set for some reason.
* @see #getBirthdate
*/
public void setBirthdate(
String newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGet1_1.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>birthdateAsDateTime</code>.
* @return The non-null value for attribute <code>birthdateAsDateTime</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
* @see #setBirthdateAsDateTime(Date)
*/
public Date getBirthdateAsDateTime(
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeSet1_1.vm
// ----------------------------------------------------------------------------
/**
* Sets a new value for the attribute <code>birthdateAsDateTime</code>.
* @param newValue The non-null new value for attribute <code>birthdateAsDateTime</code>.
* @exception JmiException If the value cannot be set for some reason.
* @see #getBirthdateAsDateTime
*/
public void setBirthdateAsDateTime(
Date newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGet1_1.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>creationDateTime</code>.
* @return The non-null value for attribute <code>creationDateTime</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
*/
public Date getCreationDateTime(
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGet1_1.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>foreignId</code>.
* @return The non-null value for attribute <code>foreignId</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
* @see #setForeignId(String)
*/
public String getForeignId(
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeSet1_1.vm
// ----------------------------------------------------------------------------
/**
* Sets a new value for the attribute <code>foreignId</code>.
* @param newValue The non-null new value for attribute <code>foreignId</code>.
* @exception JmiException If the value cannot be set for some reason.
* @see #getForeignId
*/
public void setForeignId(
String newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfOperation.vm
// ----------------------------------------------------------------------------

public org.openmdx.test.app1.cci.PersonFormatNameAsResult formatNameAs(
org.openmdx.test.app1.cci.PersonFormatNameAsParams params
) throws RefException, org.openmdx.test.app1.cci.CanNotFormatName ;

/**
*/
public org.openmdx.test.app1.accessor.jmi.PersonFormatNameAsResult formatNameAs(
String type
) throws RefException, org.openmdx.test.app1.cci.CanNotFormatName ;

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGetList.vm
// ----------------------------------------------------------------------------
/**
* Retrieves a list containing all the elements for the attribute
* <code>givenName</code>.
* @return A list containing all elements for this attribute.
* @exception JmiException If the values cannot be retrieved for some reason.
* @see #setGivenName(List)
*/
public List getGivenName(
);

/**
* Retrieves the element at the specified position in the list of all the
* values for the attribute <code>givenName</code>.
* @param index The index of the element to return.
* @return The element at the specified position in the list of all values for this attribute.
* @exception JmiException If the value cannot be retrieved for some reason.
* @see #setGivenName(int,String)
*/
public String getGivenName(
int index
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeSetList.vm
// ----------------------------------------------------------------------------
/**
* Sets a new list containing all the new elements for the attribute
* <code>givenName</code>.
* @param newValue A list containing all the new elements for this attribute.
* @exception JmiException If the values cannot be set for some reason.
* @see #getGivenName
*/
public void setGivenName(
List newValue
);

/**
* Sets a new list containing all the new elements for the attribute
* <code>givenName</code>.
* @param newValue An array containing all the new elements for this attribute.
* @exception JmiException If the values cannot be set for some reason.
*/
public void setGivenName(
String[] newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGetSet.vm
// ----------------------------------------------------------------------------
/**
* Retrieves a set containing all the elements for the attribute
* <code>group</code>.
* @return A set containing all elements for this attribute.
* @exception JmiException If the values cannot be retrieved for some reason.
* @see #setGroup(Set)
*/
public Set getGroup(
);


// ----------------------------------------------------------------------------
// Instance/IntfAttributeSetSet.vm
// ----------------------------------------------------------------------------
/**
* Sets a new set containing all the new elements for the attribute
* <code>group</code>.
* @param newValue A set containing all the new elements for this attribute.
* @exception JmiException If the values cannot be set for some reason.
* @see #getGroup
*/
public void setGroup(
Set newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGet1_1.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>lastName</code>.
* @return The non-null value for attribute <code>lastName</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
* @see #setLastName(String)
*/
public String getLastName(
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeSet1_1.vm
// ----------------------------------------------------------------------------
/**
* Sets a new value for the attribute <code>lastName</code>.
* @param newValue The non-null new value for attribute <code>lastName</code>.
* @exception JmiException If the value cannot be set for some reason.
* @see #getLastName
*/
public void setLastName(
String newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGet1_1.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>salutation</code>.
* @return The non-null value for attribute <code>salutation</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
* @see #setSalutation(String)
*/
public String getSalutation(
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeSet1_1.vm
// ----------------------------------------------------------------------------
/**
* Sets a new value for the attribute <code>salutation</code>.
* @param newValue The non-null new value for attribute <code>salutation</code>.
* @exception JmiException If the value cannot be set for some reason.
* @see #getSalutation
*/
public void setSalutation(
String newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeGet1_1.vm
// ----------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>sex</code>.
* @return The non-null value for attribute <code>sex</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
* @see #setSex(short)
*/
public short getSex(
);

// ----------------------------------------------------------------------------
// Instance/IntfAttributeSet1_1.vm
// ----------------------------------------------------------------------------
/**
* Sets a new value for the attribute <code>sex</code>.
* @param newValue The non-null new value for attribute <code>sex</code>.
* @exception JmiException If the value cannot be set for some reason.
* @see #getSex
*/
public void setSex(
short newValue
);

// ----------------------------------------------------------------------------
// Instance/IntfOperation.vm
// ----------------------------------------------------------------------------

public org.openmdx.generic.accessor.jmi.Void voidOp(
org.openmdx.base.cci.Void params
) throws RefException ;

/**
*/
public org.openmdx.generic.accessor.jmi.Void voidOp(
) throws RefException ;

// ----------------------------------------------------------------------------
// Instance/IntfEnd.vm
// ----------------------------------------------------------------------------
}



2.2 Development Phase

In the development phase, the interfaces generated by the MOF mappings have to be implemented, i.e. all methods of the interface 'Person' have to be implemented. This step can be quite time-consuming because the interfaces specify typically many methods (one or more getter/setter for each attribute, getter for references, etc.). openMDX allows to dramatically reduce the development effort by providing a library of generic, model-driven plugins. Some examples are the persistence, audit and security plugin.

Figure 3: JMI and XMI mapping of model and implementation of application plugin



Figure 4 shows how the plugins are grouped to a 'logical component', also called provider. Typically a provider is composed of an application plugin and a persistence plugin. The application plugin implements and intercepts the 'derived and behavioral' features and delegates the 'persistent' feature requests to the persistence plugin.



The reuse of existing plugins allows ultra-fast prototyping of applications with very short roundtrip times. However, the use of the standard plugins is not mandatory. They can be replaced by customized implementations as long as they implement the modeled interface.

Figure 4: A platform-independent provider consists of 1 or more plugins

A logical component (or provider) consists of the following parts:

  • the openMDX runtime

  • generic, model-driven plugins

  • the component's interface mapped to XMI and JMI

  • the manually implemented application-logic

The sample below shows the implementation of the class Person implementing the derived and behavioral features using the JMI binding.

Listing 2: Implementation of the class Person

package org.openmdx.test.test.app1.plugin.jmi;

import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.jmi.reflect.JmiException;
import javax.jmi.reflect.RefClass;
import javax.jmi.reflect.RefException;

import org.openmdx.base.accessor.jmi.cci.JmiServiceException;
import org.openmdx.base.accessor.jmi.cci.RefObject_1_0;
import org.openmdx.base.accessor.generic.cci.Object_1_0;
import org.openmdx.base.event.InstanceCallbackEvent;
import org.openmdx.base.event.InstanceCallbackListener;
import org.openmdx.base.exception.BasicException;
import org.openmdx.base.exception.BasicExceptionCode;
import org.openmdx.base.exception.ServiceException;
import org.openmdx.base.cci.basePackage;
import org.openmdx.test.app1.cci.CanNotFormatName;
import org.openmdx.test.app1.cci.PersonAssignAddressParams;
import org.openmdx.test.app1.cci.PersonFormatNameAsParams;
import org.openmdx.test.app1.cci.PersonFormatNameAsResult;
import org.openmdx.test.app1.cci.app1Package;

//---------------------------------------------------------------------------
public class PersonImpl
extends org.openmdx.test.app1.cci.PersonImpl
implements InstanceCallbackListener {

//---------------------------------------------------------------------------
public PersonImpl(
Object_1_0 object,
RefClass refClass
) {
super(object, refClass);
}

//---------------------------------------------------------------------------
public Set refDefaultFetchGroup(
) {
Set group = super.refDefaultFetchGroup();
group.add("age");
group.add("creationDateTime");
return group;
}

//---------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>age</code>.
* @return The non-null value for attribute <code>age</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
*/
public short getAge(
) throws JmiException {
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
int birthdateYear = new Integer((this.getBirthdate()).substring(0,4)).intValue();
return (short)(currentYear - birthdateYear);
}

//---------------------------------------------------------------------------
/**
* Retrieves the value for the attribute <code>creationDateTime</code>.
* @return The non-null value for attribute <code>creationDateTime</code>.
* @exception JmiException If the value cannot be retrieved for some reason.
*/
public Date getCreationDateTime(
) {
return this.getCreatedAt();
}

//---------------------------------------------------------------------------
/**
* Sets a new value for the attribute <code>salutation</code>.
* @param newValue The non-null new value for attribute <code>salutation</code>.
* @exception JmiException If the value cannot be set for some reason.
* @see #getSalutation
*/
public void setSalutation(
String newValue
) {
// System.out.println("setting salutation to " + newValue);
super.setSalutation(newValue);
}

//---------------------------------------------------------------------------
/**
* Retrieves a list containing all the elements for the reference <code>matchingAddress</code>.
* @return A list containing all the elements for this reference.
* @exception JmiException If the values cannot be retrieved for some reason.
*/
public Collection getMatchingAddress(
) throws JmiException {
return null;
}

//---------------------------------------------------------------------------
public PersonFormatNameAsResult formatNameAs(
PersonFormatNameAsParams params
) throws CanNotFormatName {

// default format "Standard"
if((params.getType() == null) || "Standard".equals(params.getType())) {
StringBuffer givenNames = new StringBuffer();
int ii = 0;
for(
Iterator i = this.getGivenName().iterator();
i.hasNext();
ii++
) {
if(ii > 0) {
givenNames.append(" ");
}
givenNames.append((String)i.next());
}
String formattedName = this.getSalutation() + " " + givenNames.toString() + " " + this.getLastName();
return ((app1Package)this.refImmediatePackage()).createPersonFormatNameAsResult(
formattedName,
Arrays.asList(new String[]{formattedName}),
new HashSet(Arrays.asList(new String[]{formattedName})),
Arrays.asList(new String[]{formattedName})
);
}

// format not supported
else {
throw new CanNotFormatName(
BasicExceptionCode.DEFAULT_DOMAIN,
BasicExceptionCode.ASSERTION_FAILURE,
"name format not supported. Supported are [Standard]",
params.getType()
);
}
}

//---------------------------------------------------------------------------
public org.openmdx.base.cci.Void assignAddress(
PersonAssignAddressParams params
) throws RefException {
basePackage basePkg = (basePackage)this.refOutermostPackage().refPackage("org:openmdx:base");
System.out.println("assigning addresses=" + params.getAddress());
return basePkg.createVoid();
}

//---------------------------------------------------------------------------
/**
* voidOp adds the country code to all assigned austrian, swiss and german postal addresses.
*/
public org.openmdx.base.cci.Void voidOp(
org.openmdx.base.cci.Void params
) throws RefException {
basePackage basePkg = (basePackage)this.refOutermostPackage().refPackage("org:openmdx:base");
for(
Iterator i = this.getAssignedAddress().iterator();
i.hasNext();
){
RefObject_1_0 address = (RefObject_1_0)i.next();
if(address instanceof InternationalPostalAddressImpl){
InternationalPostalAddressImpl postalAddress = (InternationalPostalAddressImpl)address;
int j = Arrays.asList(COUNTRY_NAME).indexOf(postalAddress.getCountry());
if(j >= 0) {
String countryPrefix = COUNTRY_CODE[j % COUNTRY_CODE.length] + '-';
String postalCode = postalAddress.getPostalCode();
if(! postalCode.startsWith(countryPrefix)) postalAddress.setPostalCode(
countryPrefix + postalCode
);
}
}
}
return basePkg.createVoid();
}

private final static String[] COUNTRY_CODE = new String[]{
"AT", "DE", "CH"
};

private final static String[] COUNTRY_NAME = new String[]{
"Austria", "Germany", "Switzerland",
"Österreich", "Deutschland", "Schweiz",
"Autriche", "Allemagne", "Suisse",
"Austria", "Germania", "Svizzera"
};

//--------------------------------------------------------------------------
// Implements InstanceCallbackListener
//--------------------------------------------------------------------------

public void postLoad(InstanceCallbackEvent event) throws ServiceException {
//System.out.println(this.getClass().getName() + ".objPostLoad");
}

public void preStore(InstanceCallbackEvent event) throws ServiceException {
//System.out.println(this.getClass().getName() + ".objPreStore");
if(!this.refIsDeleted()) {
int sex = this.getSex();
String salutation = this.getSalutation();
if((sex == 0) && !("Herr".equals(salutation) || "Mister".equals(salutation) || "Monsieur".equals(salutation))) {
throw new JmiServiceException(
new ServiceException(
BasicExceptionCode.DEFAULT_DOMAIN,
BasicExceptionCode.ASSERTION_FAILURE,
new BasicException.Parameter[]{
new BasicException.Parameter("object", this)
},
"sex 0 implies salutation [Herr|Mister|Monsieur]"
)
);
}
}
}

public void preClear(InstanceCallbackEvent event) throws ServiceException {
//System.out.println(this.getClass().getName() + ".objPreClear");
}

public void preDelete(InstanceCallbackEvent event) throws ServiceException {
//System.out.println(this.getClass().getName() + ".objPreDelete");
}

}

//--- End of File ------------------------------------------------------------



As shown in the figure it is not required that a client uses the JMI binding. openMDX also offers a generic object binding which simplifies the implementation of generic, model-driven clients and plugins. Moreover, the openMDX object management architecture is open and flexible enough to support virtually any other mappings.

As shown in the example of the class Person, the developer 'sees' a fine-granular object model. Attribute values are managed using getters and setters. It is very important to notice, that getting a feature does not necessarily lead to a roundtrip to the provider. The object layer of openMDX implements many of the JDO patterns which allow to minimize roundtrips: objects are cached client-side and have well-defined JDO-states, objects are retrieved with a default fetch set, sets of objects are managed within optimistic or non-optimistic units of work (load, edit, store), etc. This is a big difference to CORBA or EJB stubs where each operation invocation leads to a roundtrip.



2.3 Deployment Phase

The providers are deployed in the deployment phase. The deployment of a provider is a two-step task:

  • creation of a platform-specific deployment descriptor

  • packaging of the generated MOF mappings, application plugins and openMDX runtime to a platform-specific component, e.g. an EJB

Figure 5: Deployment of a platform-independent provider to a platform and adding the platform-specific deployment descriptor

openMDX offers a wide range of deployment options out-of-the-box. E.g. a platform-independent provider can be deployed in-process, as EJB, as CORBA Service or as WebService.

3 The HelloWorld Example

This section explains how to set up the openMDX/Example HelloWorld application for the Eclipse development environment. The next sections assume that you have the following software installed:

  • apache-ant-1.7.0

  • eclipse-SDK-3.2.1

  • tomcat plugin V3

  • jakarta-tomcat-5.0.28

  • poseidon-ce-6

In addition you must download the following openMDX distributions:

  • openmdx-core-1.16.4-jre-1.5

  • openmdx-portal-1.16.4-jre-1.5

  • openmdx-example-helloworld-1.16.4-jre-1.5

Moving forward we assume that you extract the openMDX core and helloworld distributions to the folder c:\pj

HelloWorld for openMDX is a very simple application. However, it allows to show the development roundtrip, i.e. modeling, development and deployment. The model is very simple. It consists of one class HelloWorld which defines the operation sayHello. As shown in Figure 6, the model is then mapped to Java interfaces (JMI mapping) which defines the component interface of the HelloWorld provider. The HelloWorld imlementation implements the operation sayHello. As we will see later on, the implementation is a plain Java object and does not contain any platform-specifics.

Figure 6: The HelloWorld Example

The example comes with two different clients: a command-line client and an HTML servlet. Both allow to invoke the operation sayHello.

Figure 7 shows the deployment of the HelloWorld sample. The HelloWorld implementation is deployed as openMDX Provider which in turn is deployed on the openMDX LightweightContainer. The LightweightContainer runs in a Java VM. The HelloWorld command-line client runs in the same VM as the openMDX provider and accesses the sayHello operation with an in-process call. The HTML servlet is deployed on Tomcat and accesses the sayHello operation with an RMI call.

Figure 7: Deployment of the HelloWorld example

As already described in the chapter 2.3 (Deployment Phase), openMDX supports many more deployment scenarios. The HelloWorld provider could have been deployed as Enterprise Java Bean (EJB) running on an application server or CORBA service running on a Java-based ORB.

The model, the HelloWorld implementation and clients do not have to be changed when changing the deployment of an application. Also see chapter 2.2.

The LightweightContainer provides the minimal infrastructure which is required to run openMDX-based application. It allows very fast development roundtrips because no underlying platform such as an application-server is required. Clients can server can be run under Eclipse which makes debugging easy and straight forward. The next sections explain how to set up an Eclipse-based development environment for the HelloWorld application.

3.1 Building with Ant

As a first step install and build the openMDX/Example HelloWorld distribution. The READMEs of the distributions explain how to set up and build the projects.

During the build process you will see a generation process which maps the platform independent HelloWorld model to Java interfaces and XML model files. Every time you change the model (which does not happen very often because openMDX models do not contain any implementation and platform-specifics) you have to run the ant target.

After the successful build of openMDX/Core and openMDX/Example HelloWorld you are now ready to switch to the Eclipse development environment.

3.2 Eclipse Project openMDX Java2 Enterprise (jre-1.5)

In a first step you create the project openMDX Java2 Enterprise (jre-1.5). This project is a library-only project. It contains important libraries - e.g. the j2ee.jar, xercesImpl.jar, etc. - for building and running the project. This project is required as a prerequisite for the openMDX/Core and openMDX/Example HelloWorld projects.

Figure 8: Eclipse Project openMDX Java2 Enterprise (jre-1.5)

To create the project start Eclipse and select File > New > Project. This opens the dialog as shown in Figure 8. Set the fields Project name and Directory accordingly. The project is located in the directory c:\pj\openmdx-example-1.16.4\opt\openmdx-1.16.4\java2\jre-1.5\enterprise in case your project root is c:\pj and the installed openMDX version is 1.16.4. Then click Next.

You can leave the tab Source empty. Set the field Default output folder as shown below to <project>/eclipse. DO NOT select the same output folder which is used for ant which is <project>\build\jre-1.5\bin!

Figure 9: Eclipse Project openMDX Java2 Enterprise (jre-1.5)

Leave the tab Projects empty as shown below:

Figure 10: Eclipse Project openMDX Java2 Enterprise (jre-1.5)

Eclipse automatically lists all library files it has found in the project folder. It should list all files as shown below:

Figure 11: Eclipse Project openMDX Java2 Enterprise (jre-1.5)

The tab Order and Export allows you to export the library files and make them available in other projects. Select all except the JRE System library as shown below:

Figure 12: Eclipse Project openMDX Java2 Enterprise (jre-1.5)

3.3 Eclipse Project openMDX Java2 Extension (jre-1.5)

Similiar to openMDX Java2 Extension (jre-1.5) you must create the additional project openMDX Java2 Extension (jre-1.5). This project is also a library-only project. It contains extension libraries - junit.jar, velocity.jar, etc. - which are required for building and running the project. This project is required as a prerequisite for the openMDX/Core and openMDX/Example HelloWorld projects.

Figure 13: Eclipse Project openMDX Java2 Extension (jre-1.5)

To create the project start Eclipse and select File > New > Project. This opens the dialog as shown in Figure 13. Set the fields Project name and Directory accordingly. The project is located in the directory c:\pj\openmdx-example-1.16.4\opt\openmdx-1.16.4\java2\jre-1.5\extension in case your project root is c:\pj and the installed openMDX version is 1.16.4. Then click Next.

You can leave the tab Source empty. Set the field Default output folder as shown in Figure 9 to <project>/eclipse. DO NOT select the same output folder which is used for ant which is <project>\build\jre-1.5\bin!

Figure 14: Eclipse Project openMDX Java2 Extension (jre-1.5)

Leave the tab Projects empty as shown below:

Figure 15: Eclipse Project openMDX Java2 Extension (jre-1.5)

Eclipse automatically lists all library files it has found in the project folder. It should list all files as shown below:

Figure 16: Eclipse Project openMDX Java2 Extension (jre-1.5)

The tab Order and Export allows you to export the library files and make them available in other projects. Select all except the JRE System library as shown below:

Figure 17: Eclipse Project openMDX Java2 Extension (jre-1.5)

3.4 Eclipse Project openMDX Core (jre-1.5)

You are now ready to create the project for openMDX Core (jre-1.5). This project contains all the classes of the openMDX MDA framework. This project is required as a prerequisite for the openMDX/Example HelloWorld project.

Figure 18: Eclipse Project openMDX Core (jre-1.5)

To create the project start Eclipse and select File > New > Project. This opens the dialog as shown in Figure 18. Set the fields Project name and Directory accordingly. The project is located in the directory c:\pj\openmdx-example-1.16.4\opt\openmdx-1.16.4\jre-1.5\core in case your project root is c:\pj and the installed openMDX version is 1.16.4. Then click Next.

Eclipse automatically detects the source folders as shown in Figure 19. Set the field Default output folder to <project>/eclipse. DO NOT select the same output folder which is used for ant which is <project>\build\jre-1.5\bin!

Figure 19: Eclipse Project openMDX Core (jre-1.5)

In the tab Projects select the previously created projects openMDX Java2 Enterprise (jre-1.5) and openMDX Java2 Extension (jre-1.5) as shown below:

Figure 20: Eclipse Project openMDX Core (jre-1.5)

Eclipse automatically finds and lists the library files as shown in Figure 21. However, the library openmdx-core.openmdx-xmi.zip and the class folder openMDX Core (jre-1.5)/src/resource must be added manually as explained below.

Figure 21: Eclipse Project openMDX Core (jre-1.5)

Click the button Add Class Folder and select the folder src/resource as shown below. Click OK.

Figure 22: Eclipse Project openMDX Core (jre-1.5)

Click the button Add JARs and select the JAR file build/jre-1.5/model/openmdx-core.openmdx-xmi.zip as shown in Figure 23. This file was created by the ant build and contains the exported model files as zipped XML.

Figure 23: Eclipse Project openMDX Core (jre-1.5)

The tab Order and Export allows you to export the library files and make them available in other projects. Select all except the JRE System library as shown below:

Figure 24: Eclipse Project openMDX Core (jre-1.5)

3.5 Eclipse Project openMDX Example HelloWorld (jre-1.5)

Finally, we have to create the project for openMDX Example HelloWorld (jre-1.5). This project contains all the classes of the HelloWorld example.

Figure 25: Eclipse Project openMDX Example HelloWorld (jre-1.5)

To create the project start Eclipse and select File > New > Project. This opens the dialog as shown in Figure 25. Set the fields Project name and Directory accordingly. The project is located in the directory c:\pj\openmdx-example-1.16.4\helloworld in case your project root is c:\pj and the installed openMDX version is 1.16.4. Then click Next.

Eclipse automatically detects the source folders as shown in Figure 26. Set the field Default output folder to <project>/eclipse. DO NOT select the same output folder which is used for ant which is <project>\build\jre-1.5\bin!

Figure 26: Eclipse Project openMDX Example HelloWorld (jre-1.5)

In the tab Projects select the previously created projects openMDX Core (jre-1.5), openMDX Portal (jre-1.5), openMDX Java2 Enterprise (jre-1.5) and openMDX Java2 Extension (jre-1.5) as shown below:

Figure 27: Eclipse Project openMDX Example HelloWorld (jre-1.5)

Eclipse automatically finds and lists the library files as shown in Figure 28. However, the library openmdx-core.openmdx-xmi.zip must be added manually as explained below:

Figure 28: Eclipse Project openMDX Example HelloWorld (jre-1.5)

Click the button Add JARs and select the JAR file build/jre-1.5/model/openmdx-example-helloworld.openmdx-xmi.zip as shown in Figure 29. This file was created by the ant build and contains the exported model files as zipped XML.

Figure 29: Eclipse Project openMDX Example HelloWorld (jre-1.5)

The tab Order and Export allows you to export the library files and make them available in other projects. Select all except the JRE System library as shown below:

Figure 30: Eclipse Project openMDX Example HelloWorld (jre-1.5)

3.6 Eclipse Project Summary

After having created all the projects the project tree should look as shown below. Refresh and build all the projects. There should be no errors. In case of errors check the library paths and project dependencies.

Figure 31: Eclipse Project openMDX Example HelloWorld (jre-1.5)

3.7 Run Command-Line Client

After having built all projects without errors you are now ready to run the command-line client. The project contains a launch file. Open the Menu Run > Run as shown below:

Figure 32: Run Command-Line Client



The folder Java Application should list the project openMDX_example-helloworld-TestHelloWorld_1. Selet the project. In case the field Project does not show a valid project select the previously created project openMDX Example Hello World (jre-1.5) as shown in Figure 33. The field Main class must have the value org.openmdx.example.helloworld1.program.TestHelloWorld_1.

Figure 33: Run Command-Line Client



Now go to the tab Classpath. Remove all existing projects and library files. Then add the projects openMDX Java2 Enterprise (jre-1.5), openMDX Java2 Extension (jre-1.5), openMDX Portal (jre-1.5) and openMDX Core (jre-1.5). The classpath listing should look as shown below. Click Apply to save the changes:

Figure 34: Run Command-Line Client



Then click Run to run the project. The output should look like as shown below:

Figure 35: Run Command-Line Client

3.8 Run HTML Servlet

As a next exercise we will run the HelloWorld client as HTML servlet. For this purpose we will start the HelloWorld provider as a separate process under Eclipse. The process acts as RMI server and accepts requests from the servlet running under Tomcat.

As a first step we must create an Eclipse launch project for the HelloWorld provider. Open the Run dialog with Run > Run and select the project openMDX_example_helloworld_HelloWorldServer_1 as shown in Figure 36. Verify that the field Project points to the project openMDX Example Hello World (jre-1.5):

Figure 36: Run HTML Servlet



In the tab Arguments verify that the VM arguments match the values shown below:

Figure 37: Run HTML Servlet



In the tab Classpath remove all existing projects and libraries and add the projects openMDX Java2 Enterprise (jre-1.5), openMDX Java2 Extension (jre-1.5), openMDX Portal (jre-1.5) and openMDX Core (jre-1.5). The list of selected projects and libraries should be identical as shown below:

Figure 38: Run HTML Servlet

Click Apply to save the changes.



Then click Run to run the project. The output should look like as shown in Figure 39. The HelloWorld provider (or service) runs as RMI server on the openMDX LightweightContainer.

Figure 39: Run HTML Servlet



As next step we must configure the Tomcat plugin. You can configure the Tomcat settings by opening the menu Window > Preferences > Tomcat as shown below. Select the Tomcat version and home directory.

Figure 40: Run HTML Servlet

In the tab Advanced add all previously created projects to the classpath as shown below:

Figure 41: Run HTML Servlet

In the tab JVM Settings verify that the JRE is set properly. Add the following JVM parameters as shown in Figure 42:

-Djava.protocol.handler.pkgs=org.openmdx.kernel.url.protocol

-Xmx300M

Figure 42: Run HTML Servlet

Before you start Tomcat you must deploy the HelloWorld web application to the Tomcat webapps directory. Go to the directory c:\pj\openmdx-example-1.16.4\jre-1.5\helloworld\deployment-unit. The directory contains the WAR openmdx-example.war:

Figure 43: Run HTML Servlet

Open the file openmdx-example.war with an unzip utility and expand the content to the directory c:\pgm\jakarta-tomcat-5.0.28\webapps\openmdx-example as shown in below:

Figure 44: Run HTML Servlet

Finally you must copy the file helloworld\src\connector\tomcat\openmdx-example.xml to the directory c:\pgm\jakarta-tomcat-5.0.28\conf\Catalina\localhost. This registers the HelloWorld provider (service) in the Tomcat JNDI.

Figure 45: Run HTML Servlet



Finally open the file c:\pgm\jakarta-tomcat-5.0.28\conf\tomcat-users.xml and add the user guest in role openMDXUser as shown below:

Listing 3: Tomcat users

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="openMDXUser"/>
<role rolename="tomcat"/>
<role rolename="role1"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="role1" password="tomcat" roles="role1"/>
<user username="both" password="tomcat" roles="tomcat,role1"/>
<user username="guest" password="guest" roles="openMDXUser"/>
</tomcat-users>



Finally start Tomcat with the Start Tomcat icon in Eclipse. Start a browser and enter the URL http://localhost:8080/openmdx-example. Login as guest/guest. This should open a non-customized user interface to access the HelloWorld provider. The operation of interest is sayHello. Select the tab and enter en, de or fr in the field language and click OK as shown in Figure 46. You should get the hello world message in the requests language as reply.

Figure 46: Run HTML Servlet

3.9 Extending the Model

This section explains how to extend the model and add some more behaviour to the HelloWorld application. In a first step we will add the new operation sayGoodBye() and in a second step we will add the attribute hitCount to the class HelloWorld.

3.9.1 Extending the Model - sayGoodBye()

In a first step we will add the operation sayGoodBye() to the class HelloWorld. To do this we need a UML modeling tool. In the following we use the Poseidon Community Edition. You can use any other modeling tool as long as it is able to read the models.xmi file located in c:\pj\openmdx-1.7.0\example-helloworld\src\model\poseidon\models.xmi. Start Poseidon and open models.xmi as shown below:

Figure 47: Extending the Model - sayGoodBye()

After opening the model you should see a diagram as shown in Figure 48. The diagram contains two classes: HelloWorld with one operation sayHello() and the class SayHelloResult with stereotype struct. sayHello() takes one input parameter locale. The semantics of the operation is to return a language-specific hello world message.

Figure 48: Extending the Model - sayGoodBye()



We will add now the operation sayGoodBye() to the class HelloWorld. To do this select the class HelloWorld and go to the end of line of the operation sayHello(). Double-click to get into the edit mode and then press enter. Add the new operation by entering sayGoodBye(language : string) : SayGoodByeResult. Click with the mouse somewhere outside of the class to apply the changes. The HelloWorld class should look as shown below:

Figure 49: Extending the Model - sayGoodBye()



Next you must create the new class SayGoodByeResult with the stereotype struct. This new class serves as output parameter for the newly created operation sayGoodBye(). To create the new class select the package org:openmdx:example:helloworld1 in the left pane and click the right mouse button. Select Create class as shown below:

Figure 50: Extending the Model - sayGoodBye()



Select the newly created class in the left pane and add the class by selecting Add to Diagram. This adds the class to the existing diagram. Make sure that the class has Visibility public, is in the Namespace org:openmdx:example:helloworld1 and that it has the Stereotype struct. The class attributes should look as shown below:

Figure 51: Extending the Model - sayGoodBye()



As a next step you must add the attribute message to the class SayGoodByeResult. To do this select the class SayGoodByeResult and click the right mouse button. Select Create Attribute as shown below:

Figure 52: Extending the Model - sayGoodBye()



This adds an attribute with default name and type. Replace the name and type with message : string. Apply the changes. Verify that the fully qualified type of the attribute is org:w3c:string as shown below:

Figure 53: Extending the Model - sayGoodBye()



Verify the properties of the attribute message. The owner must be SayGoodByeResult, the Visibility must be public and the attribute must be Changeable as shown below:

Figure 54: Extending the Model - sayGoodBye()



Then select again the operation sayGoodByeResult() and double-click the result argument in the right pane. This opens the definition of the result argument of the operation. Verify that the type of the result argument is set to org:openmdx:example:helloworld1:SayGoodByeResult as shown in Figure 55. Typically the default is org:openmdx:example:helloworld1:HelloWorld:SayGoodByeResult which is WRONG and leads to constraint violations when exporting it.

Figure 55: Extending the Model - sayGoodBye()



Before saving the model you must delete the auto-generated types int and void in the left pane as shown in Figure 56. These types are generated automatically by Poseidon and can not be handled by the openMDX model exporter.

Figure 56: Extending the Model - sayGoodBye()



Before you can use the model within openMDX you must first save it and then run the openMDX model exporter. You must save the diagram in XMI format. Select File > Export Project to XMI and replace the existing file models.xmi as shown in Figure 57. If you save the model in the native Poseidon format .zuml it can not be imported by openMDX.

Figure 57: Extending the Model - sayGoodBye()

After saving the model you must run the openMDX model exporter. You must to this by running an ant target. Open a shell and go to the directory c:\pj\openmdx-example-1.16.4\helloworld. Run the command
ant -Dproject.platform.list="jre-1.5" deliverables
The command must run without errors. If the model exporter reports model constraint violations fix them in Poseidon, export the model and run the ant target again. A common error is that you did not remove the auto-generated types int and void as shown in Figure 56.



Now you can go back to Eclipse. Refresh the project openMDX Hello World and rebuild it. You should now see the new classes SayGoodByeResult as shown below:

Figure 58: Extending the Model - sayGoodBye()



If you open the class HelloWorld you should see the new operation sayGoodBye() as shown below:

Figure 59: Extending the Model - sayGoodBye()



Open the class org.openmdx.example.helloworld1.program.TestHelloWorld_1 and add the lines as shown below:

Figure 60: Extending the Model - sayGoodBye()

The lines to be added are:

Listing 4: Call sayGoodBye()

// sayGoodBye on this instance. The sayHello operation of HelloWorldImpl is called
SayGoodByeResult gResult = null;
gResult = helloWorld.sayGoodBye("de");
System.out.println("Client: sayGoodBye[de]=" + gResult.getMessage());
gResult = helloWorld.sayGoodBye("fr");
System.out.println("Client: sayGoodBye[fr]=" + gResult.getMessage());
gResult = helloWorld.sayGoodBye("en");
System.out.println("Client: sayGoodBye[en]=" + gResult.getMessage());



The code should compile without errors. Without implementing the new method sayGoodBye() you will get the following exception when running TestHelloWorld_1:

Listing 5: Error when running command-line client

There was 1 error:
1) Standard(org.openmdx.example.helloworld1.program.TestHelloWorld_1)org.openmdx.base.accessor.jmi.spi.RefException_1
BasicException.Entry[0]
Timestamp=2005-03-09 23:52:16.673
Class=org.openmdx.compatibility.base.dataprovider.spi.StreamOperationAwareLayer_1
Method=otherOperation
Line=163
ExceptionDomain=DefaultDomain
ExceptionCode=NOT_SUPPORTED
Param[0]: exception.class=org.openmdx.base.exception.ServiceException
Param[1]: exception.source=LightweightContainer:in-process
Param[2]: path=org::openmdx::test::helloworld1/provider/Java/segment/Standard/sayGoodBye/e28dc110-90ed-11d9-bf7c-9fefe7125fc5
Param[3]: operation=sayGoodBye
Description=Character stream retrieval not supported
Backtrace:
at org.openmdx.compatibility.base.dataprovider.spi.StreamOperationAwareLayer_1.otherOperation(StreamOperationAwareLayer_1.java:163)
at org.openmdx.compatibility.base.dataprovider.spi.StreamOperationAwareLayer_1.operation(StreamOperationAwareLayer_1.java:138)
at org.openmdx.compatibility.base.dataprovider.spi.Layer_1.operation(Layer_1.java:414)
at org.openmdx.compatibility.base.dataprovider.layer.model.Standard_1.operation(Standard_1.java:880)



This means that no configured openMDX plugin implements the operation sayGoodBye(). We must now add the implementation for it.

You must add the implementation of the operation sayGoodBye() to the class org.openmdx.example.helloworld1.plugin.application.HelloWorldImpl. Open the class in Eclipse and add the lines as shown in below:

Figure 61: Extending the Model - sayGoodBye()

To lines to be added are listed below.

Listing 6: Implementation of operation sayGoodBye()

public SayGoodByeResult sayGoodBye(
HelloWorldSayGoodByeParams params
) throws RefException {
System.out.println("Plugin: invoking sayGoodBye(language=" + params.getLanguage() + ")");
String language = params.getLanguage();
String message = null;
if("de".equals(language)) {
message = "auf wiedersehen welt";
}
else if("fr".equals(language)) {
message = "adieu monde";
}
else {
message = "good bye world";
}
return (
(org.openmdx.example.helloworld1.cci.helloworld1Package)this.refImmediatePackage()
).createSayGoodByeResult(message);
}



The source should compile without errors. You should now be able to run the client without exceptions. The results of the operation call sayGoodBye() is printed to the console.

3.9.2 Extending the Model - hitCount

Next we will extend the model by one more feature. We will add the attribute hitCount to the class HelloWorld. To do this go back to Poseidon and select the class HelloWorld. Click the right mouse button and select Create Attribute as shown below:

Figure 62: Extending the Model - hitCount



Replace the attribute name and type which is created by default with hitCount : integer as shown below:

Figure 63: Extending the Model - hitCount

Select the attribute and verify that the Owner is set to HelloWorld, the Type is set to org:w3c:integer, the Visibility is public and that the attribute is set to Changeable.

Again remove the auto-generated types void and int and export to model as XMI. Then run the ant target
ant -Dproject.platform.list="jre-1.5" deliverables
Go back to Eclipse and refresh and rebuild the openMDX Hello World project.

Run the client TestHelloWorld_1. The client should run without any errors. The newly modeled attribute hitCount is neither used by the client nor implemented by the HelloWorldImpl. So the change is transparent to the implementation. Adding an attribute to a modeled class is typically an upward-compatible model-change.

However, we now want to make use of the new attribute hitCount. The hitCount should be incremented by HelloWorldImpl each time sayHello() or sayGoodBye() is called. The client prints the hitCount at the end of the invocations.



To do this add the bold-marked lines to TestHelloWorld_1.

Listing 7: Create a HelloWorld object with hitCount set to 0

pkg.refBegin();
HelloWorld helloWorld = pkg.getHelloWorldClass().createHelloWorld();

helloWorld.setHitCount(0);
provider.addSegment(getName(), helloWorld);
pkg.refCommit();
.
gResult = helloWorld.sayGoodBye("en");
System.out.println("Client: sayGoodBye[en]=" + gResult.getMessage());

helloWorld.refRefresh();
System.out.println("Client: hitCount=" + helloWorld.getHitCount());
} catch(RuntimeServiceException e) {
AppLog.error("exception", e.getExceptionStack());
throw e;
}

.



Add the following lines to HelloWorldImpl in order to increment the hitCount.

Listing 8: Increment hitCount in method sayHello()

...
else {
message = "hello world";
}

// increment hitCount.

this.setHitCount(this.getHitCount() + 1);
.

Listing 9: Increment hitCount in method sayGoodBye()

...
else {
message = "good bye world";
}

// increment hitCount.
this.setHitCount(this.getHitCount() + 1);
.





Run the client again. You should see the following output:

Listing 10: Client output after adding operation sayGoodBye() and attribute hitCount

.Deploying HelloWorld ...
>>>> **** Start Test: Standard
getting root package...
creating HelloWorld instance...
Plugin: creating HelloWorld object factory
Plugin: instantiating HelloWorldImpl
Plugin: creating HelloWorld object factory
Plugin: instantiating HelloWorldImpl
Plugin: invoking sayHello(language=de)
Client: sayHello[de]=hallo welt
Plugin: creating HelloWorld object factory
Plugin: instantiating HelloWorldImpl
Plugin: invoking sayHello(language=fr)
Client: sayHello[fr]=bonjour monde
Plugin: creating HelloWorld object factory
Plugin: instantiating HelloWorldImpl
Plugin: invoking sayHello(language=en)
Client: sayHello[en]=hello world
Plugin: creating HelloWorld object factory
Plugin: instantiating HelloWorldImpl
Plugin: invoking sayGoodBye(language=de)
Client: sayGoodBye[de]=auf wiedersehen welt
Plugin: creating HelloWorld object factory
Plugin: instantiating HelloWorldImpl
Plugin: invoking sayGoodBye(language=fr)
Client: sayGoodBye[fr]=adieu monde
Plugin: creating HelloWorld object factory
Plugin: instantiating HelloWorldImpl
Plugin: invoking sayGoodBye(language=en)
Client: sayGoodBye[en]=good bye world
Plugin: creating HelloWorld object factory
Plugin: instantiating HelloWorldImpl
Client: hitCount=6
<<<< **** End Test: Standard
Time: 2.183
OK (1 test)



3.10 HelloWorld Configuration

So far we have only seen the platform-indepedent part of the openMDX application, i.e. the HelloWorld client, HelloWorldImpl and the helloworld1 model. In addition, openMDX-based applications require the configuration of the generic runtime engine which is explained briefly in this section. For more information refer to the openMDX Example Lab documentation.

The configuration of an openMDX component requires the creation of an application and component deployment descriptor. To make things as straightforward as possible, openMDX uses the same syntax required to configure an Enterprise Java Bean (EJB). This has the following advantages:

  • When deploying openMDX components to a J2EE platform the openMDX deployment descriptors can be reused without modification

  • When deploying openMDX components to a non-J2EE platform, openMDX allows to deploy the components using the openMDX LightweightContainer. The LightweightContainer runs on top of the underlying platform (CORBA-ORB, Java-VM, ...) and is responsible for the deployment of openMDX components.

This way, not only the model and the business-logic, but also the configuration and deployment descriptors are platform-independent and can be reused on any platform supported by openMDX. Moreover, the deployment-descriptors are well-defined by the J2EE-standard, and therefore are not openMDX-proprietary.

Let us take now the deployment descriptor ../example-helloworld/src/ear/helloworld.ear/helloword-1.jar/META-INF/ejb-jar.xml of the HelloWorld provider to explain how openMDX components are configured.

An openMDX component with minimal configuration does not implement any user-defined business-logic. In a first step the business-logic has to be implemented as standard Java classes. In a second step the implementation class names have to added to the configuration.

The business-logic is added to a provider as plugins. Plugins are standard Java classes which implement the openMDX plugin interface. A provider must have one INTERCEPTION, TYPE, APPLICATION, MODEL and PERSISTENCE plugin. The plugins implement a delegation stack: the openMDX runtime instantiates all classes during startup of a provider and invokes the method activate(). The plugins are activated bottom-up. Inbound requests are first dispatched to the INTERCEPTION plugin. Each plugin can intercept requests and either execute user-defined code or delegate the call to the next plugin in the stack. If no plugin intercepts and handles a request then it will finally be delegated to the PERSISTENCE plugin. Typically all persistent object requests are handled by the persistence plugin.

This rather static architecture and delegation pattern will be extended with openMDX V2.

The plugins are configured with the following configuration entries:

Listing 11: Configuring the plugins of a provider

<env-entry>
<env-entry-name>KERNEL/INTERCEPTION</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>org.openmdx.compatibility.base.dataprovider.layer.interception.Standard_1</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>KERNEL/TYPE</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>org.openmdx.compatibility.base.dataprovider.layer.type.Strict_1</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>KERNEL/APPLICATION</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>org.openmdx.example.helloworld1.plugin.application.HelloWorldPlugin</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>KERNEL/MODEL</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>org.openmdx.compatibility.base.dataprovider.layer.model.Standard_1</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>KERNEL/PERSISTENCE</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>org.openmdx.compatibility.base.dataprovider.layer.persistence.none.InMemory_1</env-entry-value>
</env-entry>



Moreover, the HelloWorld application plugin requires to configure the JMI implementation classes which is done with the packageImpl configuration option. These entries define that the openMDX runtime dispatches requests for helloworld1 objects to the configured implementation package org.openmdx.example.helloworld1.plugin.application.

Listing 12: Configuring the JMI implementation classes

<env-entry>
<env-entry-name>KERNEL/modelPackage[4]</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>org:openmdx:example:helloworld1</env-entry-value>
</env-entry>
...
<env-entry>
<env-entry-name>KERNEL/packageImpl[4]</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>org.openmdx.example.helloworld1.plugin.application</env-entry-value>
</env-entry>



You can find more information about the plugins and their configuration options if you look at the openMDX source files and JavaDocs of the plugin classes, e.g. org.openmdx.compatibility.base.dataprovider.layer.persistence. none.InMemory_1. The method activate() initializes the plugin, reads and verifies the configuration. The activate() method is therefore typically a very good source of documentation for the semantics of plugin configuration options.

4 Summary

This book explained the development process of openMDX: modeling, development and deployment. It gives an overview of the architecture of openMDX applications and shows that it is possible to model and implement applications in a platform-independent way.

5 Bibliography

[1] R. Dirckze and S. Iyengar, A Metamodel based approach to Model Engineering using the MOF.

[2] J. Bezivin, From Object Composition to Model Transformation with the MDA, TOOLS USA, Volume IEEE TOOLS-39, Santa Barbara, August 2001.

[3] D. Poole, Model-Driven Architecture: Vision, Standards And Emerging Technologies, ECOOP, 2001.

[4] J. Vlissides and A. Alexandrescu, To Code or Not to Code, Part I, C++ Report, March 2000.

[5] ECOOP '2000 Workshop on Metadata and Active Object-Models, Cannes, France, Lecture Notes in Computer Science 1852, Springer-Verlag, Heidelberg, June 2000.
@ http://www.adaptiveobjectmodel.com/ECOOP2000

[6] S. Chiba, Load-Time Structural Reflection in Java, Proceedings of ECOOP 2000, pp.311-336, Lecture Notes in Computer Science 1850, Springer-Verlag, 2000.

[7] The Common Warehouse Metamodel, Object Management Group.
@ http://www.omg.org/cgi-bin/doc?formal/01-10-01

[8] D. DÂ'Souza, Model-Driven Architecture and Integration: Opportunities and Challenges", Version 1.1.
@ http://www.catalysis.org/publications/papers/2001-mda-reqs-desmond-6.pdf

[9] The Meta-Object Protocol and Knowledge-Based Systems, Franz, Inc., December, 1997.
@ http://www.franz.com/support/tutorials/mopnkbs.lhtml

[10] J2EE Connector Architecture, JSR-16 Home Page, Sun.
@ http://java.sun.com/aboutJava/communityprocess/jsr/jsr_016_connect.html

[11] Java 2 Platform, Enterprise Edition Home Page, Sun.
@ http://java.sun.com/j2ee/

[12] Java Technology Home Page, Sun.
@ http://java.sun.com/

[13] JDBC Data Access API Home Page, Sun.
@ http://java.sun.com/products/jdbc/index.html

[14] The Java Comunity Process, Java Community Process.
@ http://java.sun.com/aboutJava/communityprocess/

[15] Java Data Mining API, JSR-73 Home Page, Java Community Process.
@ http://java.sun.com/aboutJava/communityprocess/jsr/jsr_073_jolap.html

[16] Java Metadata Interface, JSR-40 Home Page, Java Community Process.
@ http://www.jcp.org/aboutJava/communityprocess/review/jsr040/

[17] Java OLAP Interface, JSR-69 Home Page, Java Community Process.
@ http://java.sun.com/aboutJava/communityprocess/jsr/jsr_069_jolap.html

[18] G. Kiczales, J. des Rivieres, and D.G. Bobrow, The Art of the Meta-object Protocol, MIT Press.

[19] Model-Driven Architecture Home Page, Object Management Group.
@ http://www.omg.org/mda/index.htm

[20] Model-Driven Architecture: A Technical Perspective, OMG Architecture Board MDA Drafting Team, 2001.
@ http://www.omg.org/cgi-bin/doc?ab/01-02-01.pdf

[21] Meta-Object Facility (MOF), version 1.4, Object Management Group, September, 1999.
@ http://www.omg.org/technology/documents/formal/mof.htm

[22] J. Poole, The Common Warehouse Metamodel as a Foundation for Active Object Models in the Data Warehousing Environment, ECOOP '2000 Workshop on Metadata and Active Object-Models, Cannes, France. Lecture Notes in Computer Science 1852, Springer-Verlag, Heidelberg, June, 2000.
@ http://www.cwmforum.org/CwmAOM.pdf
@ http://www.adaptiveobjectmodel.com/ECOOP2000/

[23] J. Rumbaugh, I. Jacobson, and G. Booch, The Unified Modeling Language Reference Manual, Addison-Wesley, 1995.

[24] D. Tolbert, CWM: A Model-Based Architecture for Data Warehouse Interchange, Workshop on Evaluating Software Architectural Solutions 2000, University of California at Irvine, May 2000.
@ http://www.cwmforum.org/uciwesas2000.htm

[25] Java UML/EJB Mapping Specification, JSR-26 Home Page, Sun.
@ http://java.sun.com/aboutJava/communityprocess/jsr/jsr_026_uml.html

[26] XML Metadata Interchange Specification, Version 1.