Skip to content
This repository has been archived by the owner on Jul 9, 2022. It is now read-only.

DSL Developer's Guide

dturanski edited this page Oct 30, 2012 · 5 revisions

Introduction

This page is intended for developers wishing to write a Spring Integration Groovy DSL extension for an adapter module. The DSL project structure closely mirrors Spring Integration itself. At last count, SI contains about 26 separate modules each maintained as a separate sub project and deployed in a separate jar file. In addition, there is an extensions project for community contributions. Adding all these features to the DSL is a daunting task, so community contributions are most welcome!

If you are interested in contributing a DSL module, this guide will give you an overview of what is required. Additionally, it is a good idea to study the existing adapter modules, as well as the implementation of core components to familiarize yourself with the overall structure and patterns. It's still early days, so there's always room for improvement in the core DSL framework as well.

DSL Framework Overview

The DSL is designed to be extensible. The core module provides hooks to extend the DSL itself and invoke the various internal components required by a module. Obviously, a good place to start is to look at the source for the existing adapters. The initial DSL release included adapters for jms, http, and amqp written in Groovy although it should be possible to write some of these components in Java as well - at least that's a design goal (The exception being the DOM Builder which benefits from Groovy markup builder).

The core IntegrationBuilder implements the well-known Groovy builder pattern by extending Groovy's built in FactoryBuilderSupport. It's a good idea to familiarize yourself with that if necessary. FactoryBuilderSupport is a framework that allows you to register a Factory (extends AbstractFactory). For the DSL, you should typically extend IntegrationComponentFactory instead. This requires you to implement

protected abstract Object doNewInstance(FactoryBuilderSupport builder, Object name, Object value,     Map attributes)

which provides all the builder context required to create a backing object for each builder method you define. You bind your factory to the method name by registering it with the builder, for example, in Groovy:

builder.registerFactory 'httpGet', new HttpOutboundFactory()

Currently the factory should create an instance of IntegrationComponent or one of its subclasses such as SimpleEndpoint, MessageProducingEndpoint, or GatewayEndpoint. This type hierarchy provides some simple attribute validation hooks. For example:

class HttpOutbound extends GatewayEndpoint {
   static requiredAttributes = ['url']
   Class responseType

   protected String defaultNamePrefix(){
      '$httpOBGW'
   }
}

The HttpOutbound endpoint is a simple value object. GatewayEndpoint expects requestChannel and replyChannel instead of inputChannel and outputChannel. The requiredAttributes list is known to the validation framework, and each component type should define a unique namespace prefix, beginning with '$' by convention. The DSL model is then consumed by IntegrationDomSupport which generates the XML. IntegrationDomSupport also registers builders, responsible for XML generation for the Spring Integration component. The DOM builder must be a subclass of IntegrationComponentDomBuilder.

The IntegrationBuilder looks for a class named IntegrationBuilderModuleSupport in the package : org.springframework.integration.dsl.groovy.<module-name>.builder. This class extends AbstractIntegrationBuilderModuleSupport and requires the implementation of three methods:

class IntegrationBuilderModuleSupport extends AbstractIntegrationBuilderModuleSupport {
    @Override
    void registerBuilderFactories(IntegrationBuilder builder) {
         builder.registerFactory 'httpGet', new HttpOutboundFactory()
    }

    @Override
    void registerDomBuilders(IntegrationDomSupport integrationDomSupport) {
       integrationDomSupport.registerDomBuilder HttpOutbound.class,
             new HttpOutboundDomBuilder()
    }

    @Override
    void registerNamespaces(XMLNamespaceSupport namespaceSupport) {
         namespaceSupport.addIntegrationNamespace 'int-http'	  
    }
}

As you probably have figured out, registerBuilderFactories is how you register your factories and registerDomBuilders adds your DOM builders which IntegrationDomSupport invoke to insert XML fragments into the markup stream. The registerNamespaces method is where you register required namespaces. The DOM builder must implement the doBuild() method:

/** 
 * @param builder StreamingMarkupBuilder
 * @param applicationContext the Spring ApplicationContext
 * @param component the IntegrationComponent
 * @param an optional closure containing additional XML markup used to generate child elements if necessary
 */
 	protected abstract void doBuild(Object builder, ApplicationContext applicationContext, IntegrationComponent component, Closure closure);

The builder in this case is a StreamingMarkupBuilder used to generate XML. The applicationContext is available in case you need to register beans programmatically, as is the case for closure invoking handlers. The component is the IntegrationComponent instance which supplies the metadata needed to generate the corresponding XML.

The closure parameter supplies optional groovy markup that may be executed by builder to create child elements (see RouterDomBuilder handling of channel maps for example). Currently, if your DOM Builder is expecting a closure, you should invoke at the appropriate point in the stream. For example, if the closure generates child nodes of an element named parent:

   closure.delegate = builder

   builder.parent {
       closure.call()
   } 

IntegrationComponent contains an attributes map used to capture any named parameters passed to the DSL method that are not explicitly declared as a component property. In other words, these are assumed to be attributes defined in the XML namespace. The IntegrationComponent.propertyMissing() method checks that a an undefined property has a well-formed name, and converts the name from camelCase to hyphenated lowercase if necessary, following Springs XML attribute naming conventions. Normally these can be passed directly to the XML builder.

  builder.someElement(component.attributes) 

Attributes may be preprocessed as needed, but the DSL does not perform any semantic validation as the XML parser will catch any which are invalid.

Clone this wiki locally