Articles     Forum     Examples     Download     Open Source

 

Domain Specific Modeling/Language (DSM/DSL) in IoC Frameworks

Ke Jin

ke-jin.blogspot.com

Oct 17, 2007

 

Copyright(c) 2007 by Pocomatic Software. All Rights Reserved.

 

Why and What of DSM/DSL in IoC frameworks

As discussed in the article Why and What of Inversion of Control, the IoC technology shifts the plumbing complexities of object-oriented applications into dynamically reconfigurable component frameworks and declarative application descriptions. This allows application developers or even domain users to focus on implementing high level business logic and flexibly assemble up their applications without relying on contrived low level techniques, assuming application description schemas are reasonably expressive.

 

Nevertheless, application descriptions in core schemas of IoC frameworks are more or less plain C/C++ or java function invocations expressed in XML. For instance, assuming a GPS device consists of three software components wired up as in the following diagram:

 

As illustrated in the tutorial example, using the core IoC schema of PocoCapsule, the setup of this GPS device application can be described by the following XML stanza (examples/basic-ioc/gps/setup.xml):

 

 

<bean id=”gps-locator” class=”GPSLocatorImpl”

  <ioc method=”subscribe”>

    <method-arg ref=”nav-display”/>

  </ioc>

</bean>

 

<bean id=”nav-display” class=”NavDisplayImpl”>

  <method-arg ref=”gps-locator”/>

</bean>

 

<bean id=”tick-gen” class=”TickGenImpl” lazy-init=”false”>

  <method-arg type=”short” value=”10”/>

  <method-arg type=”short” value=”1”/>

  <ioc method=”subscribe”>

    <method-arg ref=”gps-locator”/>

  </ioc>

 

  <ioc method="start"/>

</bean>

 

 

Intuitively, this XML stanza expresses the following imperative C++ code segment:

 

 

// instantiate the position locator

GPSLocator* locator = new GPSLocatorImpl;

 

// instantiate a position display,

// with ref of the locator to retrieve position data.

NavDisplay* display = new NavDisplayImpl(locator);

 

// subscribe the display to locator – to receive position

// change notification.

locator->subscribe(display);

 

// instantiate the tick generator, with appropriate

// configure setting: the 1st arg is the count of ticks,

// the 2nd arg the is interval (seconds) between ticks.

TickGen* tick_gen = new TickGenImpl(10, 1);

 

// subscribe the locator to the tick generator, to

// receive the position pulling interval tick.

tick_gen->subscribe(locator);

 

// start the gps device

tick_gen->start();

 

 

Such a low level schema has the advantages of being compact, straightforward, and generically applicable to virtually all practical problem domains. However, the compactness and generality come with the weaknesses of poor expressiveness and involving low level programming signatures (APIs). Therefore, IoC core schemas are error-prone and not desirable for domain users. For instance, the tick generator in the GPS example is to be instantiated using TickGenImpl class constructor with two arguments. From the IoC declaration and the equivalent C++ code, one would not able to tell the meaning and intention of these parameters. If the argument types were wrongly declared, the error could certainly be caught by the underlying reflection engine, however, with an error message hardly make sense for high level domain users. In a worse case, if their types of these two arguments were correctly declared but their values were accidentally swapped, it would hardly be noticed by users and not detectable by the container. Some attempts such as using property setters instead of multi-argument constructors/factories might address some of these pitfalls. However, these kludges would introduce additional verboseness and complexities that eventually defeat the meaning of using such a framework.   

 

A better approach to address above issues is to raise the abstraction level of the framework by adding problem domain specific vocabularies and higher level syntactic constructs into the IoC core schema. For instance, with a higher level schema examples/basic-ioc/dsm-gps/gps-device.dtd, the same GPS device setup could be declared in the following XML stanza (examples/basic-ioc/dsm-gps/setup.xml):

 

 

<gps-device>

  <tick-generator use=”TickGenImpl” count=”10” interval=”1”/>

  <gps-locator use=”GPSLocatorImpl”/>

  <navigation-display use=”NavDisplayImpl”/>

</gps-device>

 

 

Now, this GPS device setup description does not involve low level programming models and is much more readable by domain users. In additional, many low level syntactic errors can either be prevented in the first place or be detected by the XML parser with error messages easily comprehensible by domain users.  

 

For other applications, such as Web Services and SOA, software define radios (SDR), robot technology component applications, video games, high performance computing (HPC), telecomm network management (TMN), intelligent networks (IN/AIN), CORBA POA servers, OMG Notification/DDS participants, one could keep adding vocabularies and syntactic constructs into the core IoC schema. Eventually, the accumulated schema could be expressive for pretty generic applications, however awfully bloated and complex. This is the dilemma of expressiveness and diversity vs. intuitiveness and simplicity.

 

As a solution to this dilemma, instead of enforcing a bloated one-size-fits-all generic god schema predefined by the framework, the framework could allow and encourage users or third parties to extend or even redefine the core schema into different customized variations for their specific problem domains. These user defined schema variations are referred to as domain specific modeling or domain specific language (DSM or DSL). As each of these schemas is only designed for one specific problem domain, they are able to be simple, intuitive while still highly expressive with a relative small set of problem specific vocabularies and high level syntactic constructs. 

DSM/DSL via model transformation

A widely used stone-age approach to enable user-defined DSMs/DSLs is to explicitly modify or recreate a new application description parser for each new schema. A seemingly clever new approach is to implicitly alter/enhance the parser using user implemented plug-in handlers to process XML DOM nodes of user defined schema elements. Both of these two approaches are imperative, proprietary, and involve low level XML DOM handling. Therefore, they are costly to develop/maintain, not meant for domain users, not able to be automated/tooled, and tightly tie to the particular IoC container.

 

Modeling application high level setups with user-defined schema elements is similar to modeling business entities (objects) with user-defined classes. A class in an OO programming language captures common characters of a category of business objects. Similarly, an user-defined schema element codifies recurring setup expression of applications. User-defined classes are supported by mainstream OO languages straightforwardly, intrinsically, and portably. Especially, to compile a source code containing user-defined classes, users neither have to modify the compiler nor need to implemented compiler plug-in handlers.

 

PocoCapsule IoC framework uses the model transformation approach to support user-defined DSM/DSL schemas. It simply leverages the declarative and component model neutral IoC infrastructure itself and the standardized, mature, and ubiquitous W3C Extended Stylesheet Language Transformations (XSLT) technology. Contrary to the stone-age and the clever solutions discussed previously, this model transformation approach does not involve any proprietary plug-in API or low level XML DOM. Therefore, it is straightforward even for domain users, easily portable across IoC containers (in different core schemas), open and friendly to third party tool vendors and existing tools as well. 

 

Specifically, in this approach, for each user-defined DSM/DSL schema, the schema designer defines a set of source node to target nodes mapping templates that maps this DSM schema to a target schema, such as to the core schema. This set of templates is referred to as the stylesheet of the schema transformation. Typically, schema transformation stylesheets are also declared in XML documents (inline templates could also be supported).

 

Then, to use this user-defined DSM schema, a source application description (a XML file or string) should have the stylesheet file name (or URL) declared as its href attribute of the <?xml-transform> or <?xml-stylesheet> process instruction (PI). PocoCapsule framework will recursively perform schema transformations on the input and output application descriptions by their indicated stylesheets, until the final output result no longer has such a process instruction, presumably it has ended up with the core schema. Almost all practical DSM/DSL syntactic and semantic designs are able to be supported in this approach, because the underlying core schema is designed to support sufficiently generic C/C++ API invocation scenarios.

 

Lisp and C/C++ developers should be familiar with the scenario above. Schema transformation XSL templates in a stylesheet file are similar to Lisp defmacro S-expressions or C/C++ pre-process #define directives in a header file. The process instruction specifying the name or URL of a stylesheet in a given application description is similar to an #include directive specifying the header file in a C/C++ source code.

 

As said previously, model transformation stylesheets themselves are also XML documents typically. The default language of them is W3C XSLT. However, PocoCapsule also supports the so-called higher order transformation (HOT) that performs schema transformations on stylesheets (and even stylesheets of stylesheets, and so on) in the same way as on application descriptions. This allows users to design and use user-defined domain specific languages (DSL) to simplify their stylesheets instead of the verbose and error-prone XSLT.

A GPS example in DSM/DSL schema

The same GPS example is used to illustrate the design and use scenarios of user-defined DSM/DSL schemas in PocoCapsule. The full source code of this DSM'ized GPS example, including all C++ source code, XML/XSL documents, Makefile(s), and README document, can be found in the examples/basic-ioc/dsm-gps directory of a PocoCapsule installation.

 

In this example, the DSM/DSL schema for the GPS devices is defined as follows (see the examples/basic-ioc/dsm-gps/gps-device.dtd):

 

 

<!ELEMENT gps-device

          (tick-generator, gps-locator, navigation-display*)

 

<!ELEMENT tick-generator EMPTY>

<!ATTLIST tick-generator use      CDATA #REQUIRED>

<!ATTLIST tick-generator count    CDATA #REQUIRED>

<!ATTLIST tick-generator interval CDATA #REQUIRED>

 

<!ELEMENT gps-locator EMPTY>

<!ATTLIST gps-locator use         CDATA #REQUIRED>

 

<!ELEMENT navigation-display EMPTY>

<!ATTLIST navigation-display use  CDATA #REQUIRED>

 

 

In this DSM/DSL schema, a GPS device is declared as a composite consists of a tick-generator, a gps-locator, and arbitrary number of navigation-display components. The use attribute of these elements specifies the POCO implementation class to be used to instantiate the given component. The count and interval attributes of the <tick-generator> element specifies configuration parameters of the tick generator. All low level API signatures and component wirings are milted away. With this DSM/DSL schema, the same GPS device now can be described in the following application description (examples/basic-ioc/dsm-gps/setup.xml):

 

 

<gps-device>

  <tick-generator use=”TickGenImpl” count=”10” interval=”1”/>

  <gps-locator use=”GPSLocatorImpl”/>

  <navigation-display use=”NavDisplayImpl”/>

</gps-device>

 

   

The designer of this schema should define the stylesheet of transformation templates that transforms this DSM/DSL schema to either an intermediate schema or directly to the final core IoC schema. Each individual template specifies the macro expansion of a given individual or a set of DSM/DSL node(s) (elements and attributes) into the target schema. For instance, the following template (see examples/basic-ioc/hot/gps-device2poco.dsl) defines the macro expansion of <tick-generator> element and its attributes into the core schema of PocoCapsule:

   

 

<!--   DSL transform template for element

<tick-generator

          use=”@use” count=”@count” interval=”@interval”>

-->

<dsl-template match=”tick-generator”>

  <bean class=”@use” destroy-method=”delte” lazy-init=”false”>

    <method-arg type=”short” value=”@count”/>

    <method-arg type=”short” value=”@interval”/>

 

    <ioc method=”subscribe”>

      <method-arg ref=”gps-locator”/>

    </ioc>

 

    <ioc method=”start”/>

  </bean>

</dsl-template>

   

   

For simplicity, this transformation template itself is also declared in a more expressive and less verbose DSL schema. Templates in this schema can be transformed to W3C XSLT templates using the higher order transformation (HOT) stylesheet http://www.pocomatic.com/poco-dsl2xsl.xsl. The corresponding W3C XSL template actually looks like the following <xsl:template> declaration in the following stylesheet (examples/basic-ioc/dsm-gps/gps-device2poco.xsl):  

 

  

<!--   XSL transform template for element

<tick-generator

          use=”@use” count=”@count” interval=”@interval”>

--> 

<xsl:template match=”tick-generator”>

  <bean destroy-method=”delte” lazy-init=”false”>

    <xsl:attribute name=”class”>

      <xsl:value-of select=”@use”/>

    </xsl:attribute>

    <method-arg type=”short”>

      <xsl:attribute name=”value”>

        <xsl:value-of select=”@count”/>

      </xsl:attribute>

    </method-arg>

    <method-arg type=”short”>

      <xsl:attribute name=”value”>

        <xsl:value-of select=”@interval”/>

      </xsl:attribute>

    </method-arg>

 

    <ioc method=”subscribe”>

      <method-arg ref=”gps-locator”/>

    </ioc>

 

    <ioc method=”start”/>

  </bean>

</xsl:template>

 

   

This transformation template expressed either in DSL or in the equivalent XSL form is fairly intuitive. It implies that the <tick-generator> element in this example is to be transformed (macro expand) into the following declaration in core IoC schema:

 

           

<!-- transformed from the DSL expression

     <tick-generator use=”TickGenImpl” count=”10” interval=”1”/>

-->

<bean class=”TickGenImpl” destroy-method=”delte” lazy-init=”false”>

  <method-arg type=”short” value=”10”/>

  <method-arg type=”short” value=”1”/>

  

  <ioc method=”subscribe”>

    <method-arg ref=”gps-locator”/>

  </ioc>

 

  <ioc method=”start”/>

</bean>

 

 

For an user of this schema, to enable the desired schema transformation, the stylesheet file name or its URL should be specified in the <?xml-transform> or <?xml-stylesheet> process instruction (PI) of the input application description, for instance, as in the following description (examples/basic-ioc/dsm-gps/setup.xml):

 

           

<!-- DTD of this descriptor -->

<!DOCTYPE gps-devicce SYSTEM “file:gps-device.dtd”>

 

<!-- Process instruction (PI) specifies the stylesheet -->

<?xml-transform type=”text/xsl” href=”file:gps-device2poco.xsl”?>

 

<gps-device>

  <tick-generator use=”TickGenImpl” count=”10” interval=”1”/>

  <gps-locator use=”GPSLocatorImpl”/>

  <navigation-display use=”NavDisplayImpl”/>

</gps-device>

 

 

Then, this application description is able to be handled by the PocoCapsule/C++ core IoC container and all its utilities (such as pxgenproxy, pxencode) seamlessly as if this DSM/DSL schema was an intrinsic built-in feature of the framework.

 

To help users design their DSM/DSL schema, PocoCapsule comes with a handy utility, the pxtransform. This utility performs schema transformation recursively on the input document and writes the final result to another file or screen (when specified -E option). For instance, with the following command line:

 

 

% pxtransform setup.xml

 

 

The application description in the input file setup.xml will be transformed based on specified stylesheet in the <?xml-transform>/<?xml-stylesheet> process instruction. The output is to be written to setup_poco.xml by default.  

A framework of frameworks

By this model transformation approach, an IoC framework is able to support diverse DSMs/DSLs schemas easily. Combining with the inherent component model neutrality, an IoC and DSM/DSL integrated framework, such as PocoCapsule, turns out to be a effective framework for building other user-defined or committee-designed high level frameworks of various component models and description schemas.

 

Traditionally, these component frameworks are crafted from scratch manually with thousands or even tens of thousands lines of code and weeks or even months of efforts of a skillful framework developer. In a significant contrast, using an IoC+DSM/DSL framework, such as PocoCapsule,  an average developer or even a domain user without much OO programming discipline is able to comfortably construct the same high level component framework in just a couple of days or even hours with less than a few hundreds lines of highly descriptive and self-documented code.

  

The following standardized or user-defined component frameworks, built from IoC and DSM technologies, are presented in PocoCapsule out-of-the-box.  

These frameworks and their schema transformations to the PocoCapsule core schema are illustrated in the following diagram.

 

 

The PocoCapsule IoC&DSM framework encourages more user-defined DSM/DSL schemas than the primitive core schema. It either uses DSM/DSL as a mechanism to support high level frameworks for CORBA, OMG-DDS, Robotic Technology Component/OMG-RTC, Software Defined Radito (SDR)/JTRS-SCA, and WebServices/SCA applications, or leverages the DSM/DSL capability to enhance the core schema to support <map> and <property> in a type safe scenario. Within 25 examples of PocoCapsule, 19 of them use DSM/DSL schemas.

IoC + DSM: A paradigm shift  

The inversion of control (IoC) technique was thought to be no more than a plain-old design pattern, a lousy way of wiring up objects via dependency injection, or a test driven development (TDD) methodology. Similarly, the concept of domain specific modeling (DSM) was seen merely as a response and alternative to the to-be-all-things-to-all-people UML based MDA and CASE tools. Like its predecessors, classic DSM solutions for C++ and Java applications were typically development phase tools and focused on application generation based on high level (and usually visual) designs.

 

The combination of the model transformation based DSM and the IoC based CBD framework prompts a new paradigm of software engineering. This change can better be understood by first looking at the following three eras in database evolving history:

Now look at applications developed in classic OO languages (such as C++ and Java). From the component composition point of view, codes in such an application can be categorized as component implementations and component plumbings (e.g. instantiation, provisioning, lifecycle controling, and wiring). With this code classification, there are following three component plumbing scenarios:

It is easy to see the paradigm shift along these three plumbing scenarios is similar to the paradigm shift along the database history. Especially, in the third scenario, code of component plumbings are expressed and manipulated as data. Therefore, manipulating a DSM in this scenario is more like performing data set query/view/transformation in databases rather than code generation in traditional DSM products.

 

Comparing to the other two scenarios, the IoC + DSM scenario is much more intuitive, expressive, and error-proof. DSM schemas can largely prevent low level programming signatures to be used in application descriptions. Furthermore, these schemas can easily impose more constrained and sophisticated schema validations (such as expression structures and attribute value enumerations or ranges). Consequently, application descriptions declared in these DSM schemas are more desirable for domain users and safer than the code or annotation based configurations that (by default) rely on very primitive validations provided by the low level language type system.

 

Application wired up in other two scenarios tend to miss the forest for the trees. The imperative and/or low level nature of code logic and descriptions made it very hard for one to figure out the high level architecture design of a complex application and vice versa. Application high level refactoring/reorganizing, evolution, and maintaining were largely based on highly un-effective and unsafe methods such as reverse engineering code pattern, low level code comments, and manually generated documents. On the contrary, the declarative and domain specific nature of DSM in IoC frameworks make application high level and large scope architectures manifest and self-documented by their declarative descriptions.

 

Back to the PocoCapsule articles page

 

This page has been visited:

click here for a fancy web counter
free hit counter html code