Introduction

Introduction to openMDX

openMDX is a model-driven application framework which helps you in
writing service-oriented, MDA-based applications. Standards such as
JDO, MDA, REST and JCA build the corner stones of
openMDX. openMDX implements, combines and integrates these
standards resulting in a fully working, lightweight application framework.

Overview

At the heart of openMDX is the JDO-compliant openMDX
persistence manager. But other than most JDO persistence
managers, the openMDX persistence manager does not store objects on
the database. Instead, it acts as a proxy which is capable of
delegating to one or more (local or remote) persistence managers.
The most important features of the openMDX persistence manager are:

  • Pluggable: Plug-Ins implementing business-logic can be
    registered with the persistence manager. If an application invokes
    a method on a persistence capable object, the openMDX persistence
    manager intercepts this invocation and dispatches the call to the
    appropriate plug-in.
  • Distributable: The openMDX persistence manager can either
    delegate to local or remote persistence managers. The protocol
    used for the local communication is REST/JCA and for the remote
    communication REST/HTTP. openMDX provides the necessary
    REST infrastructure. The persistence manager is configured
    with a jdoconfig.xml configuration file. Whether local or
    remote transports are used is completely transparent to the
    application.

The openMDX persistence manager allows to write simple, distributable
and portable applications. For example a hello world client which
invokes a sayHello() service looks as follows:

import org.openmdx.example.helloworld1.*;

javax.jdo.PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("EntityManagerFactory");
javax.jdo.PersistenceManager pm = pmf.getPersistenceManager("guest", "guest");
HelloWorld helloWorld = (HelloWorld)pm.getObjectById(HELLOWORLD_XRI);
HelloWorldPackage helloWorldPkg = 
  (HelloWorldPackage)helloWorld.refOutermostPackage().refPackage(Helloworld1Package.class.getName());
pm.currentTransaction().begin();
org.openmdx.example.helloworld1.SayHelloResult hResult = helloWorld.sayHello(
  helloWorldPkg.createHelloWorldSayHelloParams("en")
);
pm.currentTransaction().commit();
System.out.println("Client: sayHello[en]=" + hResult.getMessage());

NOTE:

  • The openMDX persistence manager factory is looked up with the
    configuration-id EntityManagerFactory which is configured in
    the jdoconfig.xml.
  • The application client does not know whether the sayHello() service
    is deployed locally or remote.
  • The transaction (or unit of work) handling is either optimistic or
    non-optimistic according to the configuration of the persistence
    manager.
  • Java interfaces such as HelloWorldPackage, HelloWorld,
    SayHelloParams, SayHelloResult are generated from the
    HelloWorld UML model.
  • Each openMDX-based application is automatically a service, or more
    precisely a RESTful service. In case the HelloWorld service is
    deployed as remote service, the operation sayHello() can be invoked
    with a POST request and the resource with the XRI HELLOWORLD_XRI
    can be retrieved with a GET request by any REST client.

In addition, openMDX provides tools which are used at development time
such as the model mappers. They are responsible to map a MOF-compliant
class diagram to corresponding Java interfaces, XML schemas, JPA
classes and ORM mapping files.

Key concepts

JDO

The Java Data Objects (JDO) API is a standard interface-based Java
model abstraction of persistence, developed under the auspices of the
Java Community Process. The original JDO 1.0 is Java Specification
Request 12 (JSR 12), and the current JDO 2.0 is Java Specification
Request 243 (JSR 243). The benefits of using JDO for application
programming are:

  • Ease of use: Application programmers can focus on their domain
    object model and leave the details of persistence (field-by-field
    storage of objects) to the JDO implementation.
  • Portability: Applications written with the JDO API can be run
    on multiple implementations without recompiling or changing source
    code. Metadata, which describes persistence behavior external to
    the Java source code including most commonly used features of O/R
    mapping, is highly portable.
  • Database independence: Applications written with the JDO API
    are independent of the underlying database. JDO implementations
    support many different kinds of transactional data stores, including
    relational and object databases, XML, flat files, and others.
  • High performance: Application programmers delegate the details
    of persistence to the JDO implementation, which can optimize data
    access patterns for optimal performance.

However, the JDO and JPA (Java Persistence Architecture)
leaves an important question unanswered: where comes the business
logic? One can put all or part of the business logic into the persistence
capable (or entity) classes (or into a library which is invoked by the
persistence capable class). Or one can put an application-layer on top
of the persistence layer. However, all these approaches have drawbacks:

  • There is no clean separation between business and persistence
    logic
  • The business logic is located in the persistence tier instead of in
    the application tier
  • Introducing an application layer also introduces a different style
    of architecture and programming. Code which delegates to the
    application layer looks different than code which delegates to
    the persistence layer. This way code is layer-specific and hence
    not as portable and reusable as should be.

The openMDX persistence manager addresses these issues as follows:

  • The openMDX persistence manager is a JDO-compliant implementation
    which acts as a proxy to one or more (local or remote) persistence
    managers. By default all invocations are delegated to one of the
    configured proxies (routing is configurable).
  • The business logic is isolated into separate classes, called aspect
    oriented plug-ins (AOP). Persistence capable classes do not contain
    any business logic. Their only responsibility is to make an object
    persistent. As a consequence, the persistence capable classes can
    be generated, so no programming is required at this level. AOPs
    are registered with the openMDX persistence manager. If an
    application invokes a method of a persistence capable, the openMDX
    persistence manager intercepts the invocation and dispatches the
    call to the registered AOPs. In case no AOP intercepts the
    invocation, the call is forwarded to one of the configured delegation
    persistence mangers.
  • The openMDX persistence manager is stackable. This allows to
    split the business logic into layers, i.e. different aspects
    (business logic, generic functions such as auditing or access
    control) of the application logic can be completely separated.

REST and JCA

The openMDX persistence manager acts as a proxy persistence manager
and allows to delegate to one or more (local or remote) persistence
managers. The protocol used for communication between persistence
managers is REST:

  • Object retrievals are mapped to a GET <object xri="">
  • Queries are mapped to a GET <reference xri,="" query="">
  • Object modifications are mapped to PUT <object>
  • Object creations are mapped to POST <object>
  • Method invocations are mapped to POST <object xri,="" method,="" parameters="">
  • Object removals are mapped to DELETE <object xri="">

REST/HTTP is used for remote (inter-process) and REST/JCA for
local communication between persistence mangers. What is REST/JCA?
REST was initially described in the context of HTTP, but is
not limited to that protocol. RESTful architectures can be based
on other application layer protocols if they already provide a rich and
uniform vocabulary for applications based on the transfer of meaningful
representational state.

The Java EE Connector Architecture (JCA) ([http://jcp.org/aboutJava/communityprocess/final/jsr322/index.html JCA])
provides a suitable application layer protocol. The REST
methods GET, PUT, POST and DELETE are mapped
to corresponding REST interaction specifications. The remote
persistence manager is treated as a JCA resource. Using
REST/JCA an object retrieval looks as follows:

javax.resource.cci.Connection conn = connectionFactory.getConnection(
  new RestConnectionSpec(user, password)
);
javax.resource.cci.RecordFactory rf = connectionFactory.getRecordFactory();
javax.resource.cci.Interaction interaction = conn.createInteraction();
InteractionSpec ispec = InteractionSpecs.getRestInteractionSpecs(true).GET;
javax.resource.cci.IndexedRecord input = factory.createIndexedRecord(Multiplicities.LIST);
input.add(object_xri);
javax.resource.cci.Record output = interaction.execute(ispec, input);

Output contains the retrieved object in a JCA record.

Using REST as unified communication protocol has the following
advantages:

  • Independent deployment of components: an application using the
    openMDX persistence manager can either be deployed in-process or
    fully distributed.
  • Integration: A persistence manager configured for REST/HTTP
    can be accessed by any 3rd party REST client. This allows easy
    integration of openMDX-based applications with non-openMDX
    applications and even with non-Java applications.
  • Lightweight: The implementation of REST/HTTP and
    REST/JCA is lightweight. All that is required on client-side
    is a HttpUrlConnection and on server-side a WebApp container
    such as Tomcat.
  • Scalability: An application using the openMDX persistence
    manager is scalable out-of-the box. A persistence manager deployed
    under Tomcat can handle multiple sessions and concurrent requests
    (such as all HTTP servlets can).

MDA

Based on OMGs established standards such as

  • Unified Modeling Language (UML)
  • MetaObject Facility (MOF )
  • XML Metadata Interchange (XMI)
  • Common Warehouse Metamodel (CWM)

the OMG's Model Driven Architecture ([http://www.omg.org/mda MDA])
standards separate business and application logic from the underlying
platform technology. Platform-independent models (PIMs) of an
application or integrated systems's business functionality and
behaviour, built using UML and the other associated OMG modeling
standards, can be realized through the MDA on virtually any platform,
open or proprietary, including WebServices, .NET, CORBA,
J2EE, and others. These platform-independent models document
the business functionality and behaviour of an application separate
from the technology-specific code that implements it, insulating the
core of the application from technology.

The domain object model of an openMDX-based application
is specified by a platform-independent model (PIM). The model does not
contain any platform-specific (J2EE, WSDL, CORBA,
RPC, etc.) information. Using the MDA/PIM-approach
has the following advantages:

  • Automation: the PIM can be mapped by generators to any
    platform-specific artifact by applying the mappings specified by
    the MDA standards. E.g. the PIM can be mapped to a
    Java API by applying the JMI mapping (specified by the [http://java.sun.com/products/jmi/index.jsp Java metadata interface (JMI)]
    standard).
  • Single-source: the domain model has to be specified exactly once
    even if the application must support several platforms, e.g. expose
    the application logic as Java library, REST or RPC service.
  • Model-driven algorithms: According to the MDA standards, PIMs
    are stored in a MOF (Meta Object Facility) repository which is
    accessible to the application at runtime. This allows an
    application to inspect the model and perform generic, model-driven
    algorithms on application data.

The development process is rather straight-forward: first the business objects (BOs) are
specified by platform-independent, MOF-compliant class diagrams. These are then mapped by
openMDX-generators to Java interfaces by applying the MOF to Java mapping
(specified by the [http://java.sun.com/products/jmi/index.jsp Java metadata interface (JMI)]
standard). These interfaces define the API of the the application's domain object model.

The corresponding JMI interface is shown below:

package org.openmdx.example.helloworld1.jmi1;

public interface HelloWorld
  extends org.openmdx.example.helloworld1.cci2.HelloWorld,
    org.openmdx.base.jmi1.Segment{

  public org.openmdx.example.helloworld1.jmi1.SayHelloResult sayHello(
      org.openmdx.example.helloworld1.jmi1.HelloWorldSayHelloParams in
  );

}

A sample hello world client:

import org.openmdx.example.helloworld1.*;

javax.jdo.PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("EntityManagerFactory");
javax.jdo.PersistenceManager pm = pmf.getPersistenceManager("guest", "guest");
HelloWorld helloWorld = (HelloWorld)pm.getObjectById(HELLOWORLD_XRI);
HelloWorldPackage helloWorldPkg = (HelloWorldPackage)helloWorld.refOutermostPackage().refPackage(Helloworld1Package.class.getName());
pm.currentTransaction().begin();
org.openmdx.example.helloworld1.SayHelloResult hResult = helloWorld.sayHello(
  helloWorldPkg.createHelloWorldSayHelloParams("en")
);
pm.currentTransaction().commit();
System.out.println("Client: sayHello[en]=" + hResult.getMessage());

The hello world implementation:

package org.openmdx.example.helloworld1.aop2;

import javax.jmi.reflect.RefException;

import org.openmdx.base.aop2.AbstractObject;
import org.openmdx.example.helloworld1.jmi1.HelloWorldSayHelloParams;
import org.openmdx.example.helloworld1.jmi1.Helloworld1Package;
import org.openmdx.example.helloworld1.jmi1.SayHelloResult;

public class HelloWorldImpl
    <S extends org.openmdx.example.helloworld1.jmi1.HelloWorld,N extends org.openmdx.example.helloworld1.cci2.HelloWorld,C extends Void>
    extends AbstractObject<S,N,C> {

    public HelloWorldImpl(
        S same,
        N next
    ) {
        super(same, next);
        System.out.println("Plugin: instantiating HelloWorldImpl");
    }

    public SayHelloResult sayHello(
        HelloWorldSayHelloParams params
    ) throws RefException {
        System.out.println("Plugin: invoking sayHello(language=" + params.getLanguage() + ")");
        String language = params.getLanguage();
        String message = null;
        if("de".equals(language)) {
            message = "hallo welt";
        }
        else if("fr".equals(language)) {
            message = "bonjour monde";
        }
        else {
            message = "hello world";
        }       
        return ((Helloworld1Package)this.sameObject().refOutermostPackage().refPackage(
            Helloworld1Package.class.getName())
        ).createSayHelloResult(message);
    }

}

The openMDX persistence manager

In detail, the openMDX persistence manager works as follows:

  • At the top level there are one or more entity managers.
    Their responsibility is to dispatch API method invocations issued
    by the client application and dispatch them to the configured AOPs.
    The last entity manager in the stack delegates to the
    object view manager which manages service data objects (SDOs).
    SDOs implement a generic interface which allows to manage the
    object's state in a generic way, e.g. objSetValue(feature, value)
    or objGetValue(feature). For more information about SDOs see
    [http://www.ibm.com/developerworks/java/library/j-sdo/ Introduction to Service Data Objects].
    (NOTE: openMDX SDOs implement the interface DataObject_1_0
    which is a much simpler and more JDO- and JPA-aligned than
    originally specified by IBM). Typed JMI method invocations are
    mapped to the SDO interface. E.g. if the client application invokes
    the method Person.setName(String name), the JMI persistence manager
    first checks if any AOP implements the method. If an implementation
    is found, the call is dispatched to this AOP. If none is found, the
    call is forwarded to the corresponding SDO and mapped to the method
    objSetValue("name", value).
  • The object view manager manages SDOs. It does it more or less the
    same way as the JMI entity manager, however it allows to implement
    low-level, generic plug-ins. It also allows to register AOPs and
    dispatches method invocations. In order to be able to distinguish
    plug-ins at the different levels, plug-ins at JMI level are called
    AOP2, whereas plug-ins at object view level are called AOP1.
    The object view manager delegates to the next entity manager in
    the stack which is the data object manager.
  • The data object manager manages transactions (also called
    unit of work), the state and the life-cycle of SDOs. The data
    object manager is either delegates to a local or remote JCA
    connection. All object operations are mapped to REST interactions.

Summary

openMDX helps you in writing platform-independent applications. The
application's interface is specified with a platform independent model
(PIM) and the business logic is implemented as POJOs (plain old Java
objects) which are platform-, distribution- and persistence-technology
independent. The application can be deployed locally or distributed by
using the RESTful, JDO-compliant openMDX persistence
manager. The resulting applications are lightweight and can be deployed
on any J2SE or J2EE platform such as Tomcat/OpenEJB.