-
Notifications
You must be signed in to change notification settings - Fork 40
DSL Developer's Guide
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.
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 two methods:
class IntegrationBuilderModuleSupport extends AbstractIntegrationBuilderModuleSupport {
@Override
void registerBuilderFactories(IntegrationBuilder builder) {
builder.registerFactory 'httpGet', new HttpOutboundFactory()
}
@Override
void registerDomBuilders(IntegrationDomSupport integrationDomSupport) {
integrationDomSupport.namespaceSupport.addIntegrationNamespace('int-http')
integrationDomSupport.domBuilders['org.springframework.integration.dsl.groovy.http.HttpOutbound']
= new HttpOutboundDomBuilder(integrationDomSupport:integrationDomSupport)
}
}
As you probably have figured out, registerBuilderFactories is how you register your factories and registerDomBuilders adds your builders (XML to IntegrationDomSupport. This is also 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 attributes a Map of undeclared component properties passed as named parameters
* @param an optional closure containing additional XML markup used to generate child elements if necessary
*/
protected abstract void doBuild(Object builder, ApplicationContext applicationContext, Object component, Map attributes, Closure closure);
The builder in this case is a StreamingMarkupBuilder which makes it easy to generate XML. The applicationContext is available in case you need to register beans created with a Spring BeanDefinitionBuilder, as is the case for handling closures. The component is the IntegrationComponent instance supplying the metadata needed to generate the corresponding XML. The attributes argument is a map of any named parameters passed to the DSL method that were not explicitly declared as a property of the component. In other words, the IntegrationComponent.propertyMissing() method converts such properties from camelCase to hyphenated lowercase, following XML attribute naming conventions used by Spring. (Note: The attributes parameter may be eliminated in a future release, currently it is a reference to component.componentProperties).
The closure parameter supplies optional XML markup that may be needed to create child elements (see RouterDomBuilder handling of channel maps for example).