SourceForge.net Logo

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 prefabricated, generic, model-driven plugins. Some examples are:

  • Datastore plugin. The datastore plugin is a model-driven plugin implementing a OO-to-Relational mapping in a generic way. When using this plugin, the application-logic can be freed from any persistence management and it is sufficient to implement derived and behavioral features in the application plugins ('age' and 'assignedAddress' are derived features and 'formatNameAs' is a behavioral feature of class Person).

  • Role plugin. Implements the role pattern as described in [43] without having to implement the 'role features' in the application code.

  • State plugin. Allows to model and manage stated objects without having to implement the 'state features' in the application code.

Figure 2-3 shows how the plugins are grouped to a 'logical component', also called provider. Typically a provider is composed of an application plugin and a datastore plugin. The application plugin implements and intercepts the 'derived and behavioral' features and delegates the 'persistent' features to the datastore 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 2-3. A platform-independent provider is composed of one or more plugins.

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

  • the openMDX runtime

  • prefabricated, 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.

Example 2-2. Implementation of 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 leads 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.