Nav
You are viewing an older version of this section. Click here to navigate to the latest version.

Creating a Custom XML Namespace

XML schema definitions are used for each module to define the objects and properties that the module makes available to Mule ESB. These configuration elements are introduced using a namespace for each module and associating the namespace with the schema. This page describes how configuration is handled in Mule and what steps are required when writing a new module or transport in Mule.

Advantages of Using Namespaces

The use of namespaces provides the following advantages:

  • Class names are removed from XML so that implementation details are hidden.

  • All objects introduced by the module are self-contained by a namespace.

  • The schema provides a domain-specific language (DSL) for the module where all objects and properties are described in the schema with type validation.

  • The schema can provide additional validation for types, value ranges, and required properties.

Using Module Schemas

Schemas are located in each package’s main/resources/META-INF directory. The core schema is in the mule-core package, the TCP schema is in the tcp package, and so on.

The Mule schema can be used directly or embedded inside Spring. In addition, Spring beans can be created directly inside the Mule schema (just use <spring:bean …​/>) and elements from other namespaces can be placed in <other>…​</other>.

Mule Namespace

As of Mule 3.0, version is no longer part of the namespace declaration. Also, "mulesource" is replaced with "mulesoft" in all schema and xml config files.

The default namespace for Mule xml configuration files is mule:


          
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:spring="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
       http://www.mulesoft.org/schema/mule/core/3.0 META-INF/mule.xsd">
  <!-- Mule config here -->
  <spring:bean ...you can also embed Spring bean definitions directly.../>
 
  <spring:beans>
    <!-- and you can have nested spring definitions -->
  </spring:beans>
</mule>

Note here we have a spring namespace declared so we can embed spring beans directly inside your Mule configuration file.

More on Mixing Mule and Spring

The Mule schema includes the ability to use Spring elements at certain points by including <spring:bean> inside <mule>. These elements are handled explicitly by Mule code, which delegates their processing to Spring.

Be careful when using Spring elements in your own schema, and check that they behave as expected. The <bean> and <beans> elements are all forwarded to Spring for processing. In addition, the predefined mule:mapType can be used and, when associated with the ChildMapDefinitionParser, will automatically construct a map using Spring’s <entry> elements (this is the only way that <entry> can be used directly inside Mule elements). For examples, see the use of mapType in the Mule schema. Similar behavior with ChildPropertiesDefinitionParser should also be possible (but ChildMap_Entry_ and ChildList_Entry_DefinitionParsers are unrelated to Spring).

Other namespaces can be introduced via <spring:beans> or by adding a dedicated element in a module (see the Scripting module’s <lang> element).

Documentation

You add documentation to the schema using the <xsd:annotation> and <xsd:documentation> tags:


          
       
1
2
3
4
5
6
7
8
9
10
<xsd:element name="my-element" type="myType">
  <xsd:annotation>
    <xsd:documentation>This element does this</xsd:documentation>
  </xsd:annotation>
</xsd:element>
<xsd:complexType name="myType">
  <xsd:annotation>
    <xsd:documentation>This type does that</xsd:documentation>
  </xsd:annotation>
</xsd:complexType>

While documentation can be added in various places within the schema, tools that use this information follow certain conventions (see below). As a consequence, embedded documentation should:

  • Be placed in the element, attribute, or associated type

  • Avoid duplicating information in element and type

  • Avoid reference elements (<xsd:element ref="…​"/>)

  • Make documentation at each level correct and distinct (do not rely on inheritance, but try to avoid duplication)

IntelliJ Idea

Idea will show documentation defined for an element or attribute, or for the associated type if those are missing. The information is displayed when the user presses Ctrl-J. For more information see this post about how to work with Mule schemas in IntelliJ.

Eclipse

The Web Tools Platform (WTP) XML editor shows documentation defined for an element or attribute, or for the associated type if those are missing. The information is displayed when you press F2 when an element or attribute is selected or has the cursor on it. The same information is also shown when using the context-sensitive auto-completion functionality by pressing the "CTRL-." key combination.

The WTP XML editor will display "inherited" documentation but does not show documentation associated with referenced global elements.

Writing Configuration Handlers

When writing a new Mule transport of module, you will need to write a schema definition and the code necessary to parse the XML, but most of the work is done for you. The following section will walk through the process of:

  • Defining the XML Schema: Describes all the objects that your module exposes, such as transformers, components, filters, routers, agents, etc.

  • Writing the Namespace Handler: Responsible for configuring the XML parsers for each of the elements that your schema defines.

  • Writing a Definition Parser for each of the elements (objects) defined in the schema

  • Testing your Namespace Handler

Defining the Schema

If you are not familiar with XML schema, you may want to take an introductory course here. However, Mule defines most of what you need out of the box, so it’s fairly straightforward to jump in and write your own. Following are a few key concepts:

  • Complex Types are defined for each object in the module. Complex types define the elements and attributes that make up the type. For example, a connectorType would define shared attributes for all connectors and define any nested elements such as <service-overrides>.

    
                 
              
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    <xsd:complexType name="connectorType" mixed="true">
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
                <xsd:element name="receiver-threading-profile" type="threadingProfileType" minOccurs="0"
                             maxOccurs="1"/>
                <xsd:element name="dispatcher-threading-profile" type="threadingProfileType" minOccurs="0"
                             maxOccurs="1"/>
                <xsd:group ref="exceptionStrategies" minOccurs="0" maxOccurs="1"/>
                <xsd:element name="service-overrides" type="serviceOverridesType" minOccurs="0" maxOccurs="1"/>
            </xsd:choice>
     
            <xsd:attribute name="name" type="xsd:string" use="required"/>
            <xsd:attribute name="createDispatcherPerRequest" type="xsd:boolean"/>
            <xsd:attribute name="createMultipleTransactedReceivers" type="xsd:boolean"/>
    </xsd:complexType>

Note that complex types can be extended (much like inheritance), so new complex types can be built upon existing ones. Mule provides a number of base complex types out of the box for connectors, agents, transformers, and routers. If you write one of these, your schema should extend the corresponding complex type. Using TCP as an example, here is an excerpt from where we define the noProtocolTcpConnectorType:


          
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<xsd:import namespace="http://www.mulesoft.org/schema/mule/core/3.0"/>
 
<xsd:complexType name="noProtocolTcpConnectorType">
  <xsd:complexContent>
    <xsd:extension base="mule:connectorType">
      <xsd:attribute name="sendBufferSize" type="mule:substitutableInt">
        <xsd:annotation>
          <xsd:documentation>
            The size of the buffer (in bytes) used when sending data, set on the socket itself.
          </xsd:documentation>
        </xsd:annotation>
      </xsd:attribute>
      <xsd:attribute name="receiveBufferSize" type="mule:substitutableInt">
        <xsd:annotation>
          <xsd:documentation>
            The size of the buffer (in bytes) used when receiving data, set on the socket itself.
          </xsd:documentation>
        </xsd:annotation>
      </xsd:attribute>
      ...
      <xsd:attribute name="validateConnections" type="mule:substitutableBoolean">
        <xsd:annotation>
          <xsd:documentation>
            This "blips" the socket, opening and closing it to validate the connection when first accessed.
          </xsd:documentation>
        </xsd:annotation>
      </xsd:attribute>
    </xsd:extension>
  </xsd:complexContent>
</xsd:complexType>

This complex type extends the mule:connectorType type. Notice that we need to import the Mule core schema since that is where the `connectorType`is defined.

Schema Types

Note that the types we use for int, boolean, and all numeric types are custom types called substitutableInt or substitutableBoolean. These types allow for int values and boolean values but also allow developers to use property placeholders, such as ${tcp.keepAlive} as a valid value for the property. These placeholders will be replaced at run-time by real values defined in property files.

Element definitions describe what elements are available in the schema. An element has a type, which should be declared as a Complex Type. For example:


          
       
1
<xsd:element name="connector" type="tcpConnectorType"/>

This makes the connector element available within the tcp namespace.

The schema should be called mule-<short module name>.xsd and stored in the META-INF of the module or transport.

Versioning

In Mule, the version of the schema is maintained in the schema URI. This means that the namespace and the targetNamespace implicitly contain the schema version. Schema URIs use the following convention:


          
       
1
http://www.mulesoft.org/schema/mule/core/3.0

The first part of the URI – http://www.mulesoft.org/schema/mule/ – is the same for each schema. It is then followed by the module’s short name, followed by the version of the schema.

Schema Mapping

To stop the XML parser from loading Mule schemas from the Internet, you add a mapping file that maps the remote schema location to a local classpath location. This mapping is done in a simple properties file called spring.schemas located in the META-INF directory for the module/transport.

spring.schemas


          
       
1
http\://www.mulesoft.org/schema/mule/tcp/3.0/mule-tcp.xsd=META-INF/mule-tcp.xsd

Namespace Handler

The namespace handler is responsible for registering definition parsers, so that when an element in the configuration is found, it knows which parser to use to create the corresponding object.

A namespace handler is a single class that is directly associated with a namespace URI. To make this association, there needs to be a file called spring.handlers in the root of the META-INF directory of the module or transport. The file contains the following:

spring.handlers


          
       
1
http\://www.mulesoft.org/schema/mule/tcp/3.0=org.mule.transport.tcp.config.TcpNamespaceHandler

The TcpNamespaceHandler code is very simple because there is a base support class provided:

TcpNamespaceHandler.java


          
       
1
2
3
4
5
6
7
public class TcpNamespaceHandler extends NamespaceHandlerSupport
{
    public void init()
    {
        registerBeanDefinitionParser("connector", new OrphanDefinitionParser(TcpConnector.class, true));
    }
}

Here, there should be one or more registrations binding an element name with a definition parser.

Definition Parsers

The definition parser is where the actual object reference is created. It includes some Spring-specific classes and terminology, so it’s worth reading this introduction.

Mule already includes a number of useful definition parsers that can be used for most situations or extended to suit your needs. You can also create a custom definition parser. The following table describes the existing parsers. To see how they are used, see org.mule.config.spring.handlers.MuleNamespaceHandler.

Parser Description

               
            
1
org.mule.config.spring.parsers.generic.OrphanDefinitionParser

Contructs a single, standalone bean from an element. It is not injected into any other object. This parser can be configured to automatically set the class of the object, the init and destroy methods, and whether this object is a singleton.


               
            
1
org.mule.config.spring.parsers.generic.ChildDefinitionParser

Creates a definition parser that will construct a single child element and inject it into the parent object (the enclosing XML element). The parser will set all attributes defined in the XML as bean properties and will process any nested elements as bean properties too, except the correct definition parser for the element will be looked up automatically. If the class is read from an attribute (when class is null), it is checked against the constraint. It must be a subclass of the constraint.


               
            
1
org.mule.config.spring.parsers.generic.ParentDefinitionParser

Processes child property elements in XML but sets the properties on the parent object. This is useful when an object has lots of properties and it’s more readable to break those properties into groups that can be represented as a sub-element in XML.


               
            
1
org.mule.config.spring.parsers.collection.ChildMapEntryDefinitionParser

Allows a series of key value pair elements to be set on an object as a Map. There is no need to define a surrounding 'map' element to contain the map entries. This is useful for key value pair mappings.


               
            
1
org.mule.config.spring.parsers.AbstractHierarchicalDefinitionParser

This definition parser introduces the notion of hierarchical processing to nested XML elements. Definition parsers that extend this class are always child beans that get set on the parent definition parser. A single method getPropertyName must be overriden to specify the name of the property to set on the parent bean with this bean. Note that the property name can be dynamically resolved depending on the parent element. This implementation also supports collections and Maps. If the bean class for this element is set to MapEntryDefinitionParser.KeyValuePair, it is assumed that a Map is being processed and any child elements will be added to the parent Map.


               
            
1
org.mule.config.spring.parsers.AbstractMuleBeanDefinitionParser

This parser extends the Spring provided AbstractBeanDefinitionParser to provide additional features for consistently customizing bean representations for Mule bean definition parsers. Most custom bean definition parsers in Mule will use this base class. The following enhancements are made:

  • Attribute mappings can be registered to control how an attribute name in Mule XML maps to the bean name in the object being created.

  • Value mappings can be used to map key value pairs from selection lists in the XML schema to property values on the bean being created. These are a comma-separated list of key=value pairs.

  • Provides an automatic way of setting the init-method and destroy-method for this object. This will then automatically wire the bean into the lifecycle of the application context.

  • The singleton property provides a fixed way to make sure the bean is always a singleton or not.

Naming Conventions

The number and variety of definition parsers is growing rapidly. To make them more manageable, please use the following conventions.

  • Group by function. Abstract bases live in org.mule.config.spring.parsers. Under that we have generic, specific, and collection, which should be self-explanatory. Inside those you may want to add further grouping (e.g., specific.security).

  • Use consistent names for the relationship of the object being created with the surrounding context:

    • Child objects are injected into parents (the enclosing DOM element)

    • Grandchild are like child, but recurse up the DOM tree more than one generation

    • Orphan objects stand alone

    • Named objects are injected into a target identified by name rather than DOM location.

    • Parent definition parsers are something like facades, providing an alternative interface to the parent.

Testing

Testing the namespace handler is pretty simple. You configure the object in Mule XML, start the server, and check that the values have been set correctly. For example:


          
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TcpNamespaceHandlerTestCase extends FunctionalTestCase
{
    protected String getConfigResources()
    {
        return "tcp-namespace-config.xml";
    }
 
    public void testConfig() throws Exception
    {
        TcpConnector c = (TcpConnector) muleContext.getRegistry().lookupConnector("tcpConnector");
        assertNotNull(c);
        assertEquals(1024, c.getReceiveBufferSize());
        assertEquals(2048, c.getSendBufferSize());
        assertEquals(50, c.getReceiveBacklog());
        assertEquals(3000, c.getReceiveTimeout());
        assertTrue(c.isKeepAlive());
        assertTrue(c.isConnected());
        assertTrue(c.isStarted());
 
    }
}

Extending Existing Handlers

Instead of creating a new handler, you can extend an existing transport and add new properties and elements. For example, the SSL transport extends the TCP transport.


         
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.mulesoft.org/schema/mule/ssl/2.2"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:mule="http://www.mulesoft.org/schema/mule/core/2.2"
            xmlns:tcp="http://www.mulesoft.org/schema/mule/tcp/2.2"
            targetNamespace="http://www.mulesoft.org/schema/mule/ssl/2.2"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">
 
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
    <xsd:import namespace="http://www.mulesoft.org/schema/mule/core/2.2"
                schemaLocation="http://www.mulesoft.org/schema/mule/core/2.2/mule.xsd" />
    <xsd:import namespace="http://www.mulesoft.org/schema/mule/tcp/2.2"
                schemaLocation="http://www.mulesoft.org/schema/mule/tcp/2.2/mule-tcp.xsd"/>
 
    <xsd:element name="connector" substitutionGroup="mule:abstract-connector">
        <xsd:annotation>
            <xsd:documentation>
                Connect Mule to an SSL socket, to send or receive data via the network.
            </xsd:documentation>
        </xsd:annotation>
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="tcp:tcpConnectorType">
                    <xsd:sequence>
                        <xsd:element minOccurs="0" maxOccurs="1" name="client" type="mule:tlsClientKeyStoreType"/>
                        <xsd:element minOccurs="0" maxOccurs="1" name="key-store" type="mule:tlsKeyStoreType"/>
                        <xsd:element minOccurs="0" maxOccurs="1" name="server" type="mule:tlsServerTrustStoreType"/>
                        <xsd:element minOccurs="0" maxOccurs="1" name="protocol-handler" type="mule:tlsProtocolHandler"/>
                    </xsd:sequence>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

Simple Recipe

The following recipe is sufficient for a simple transport (like UDP). The ordering helps guarantee complete coverage.

  1. Write a test case for the connector.

  2. Use IDE’s auto completion to test each public getter (as a first approximation to the public API - tidy by hand).

  3. Set the test value to something other than the default.

  4. Write the XML configuration for the connector (test/resources/foo-connector-test.xml) using the properties from the test (make sure the import section is correct).

  5. Write the schema definition (tweaking until the XML connector config shows no errors) (META-INF/mule-foo.xsd).

  6. Write the namespace handler (and any needed definition parsers) (src/main/java/org/mule/providers/foo/config/FooNamespaceHandler)

  7. Set the Spring handler mapping (META-INF/spring.handlers).

  8. Set the local schema mapping (META-INF/spring.schemas).

  9. Make sure the test runs.

  10. Check properties against the documentation and make consistent (but note that things like connection strategy parameters are handled by an embedded element that is itself inherited from the connectorType) and then re-run the test.

Resources

  • A useful set of PDF slides that give an overview of the new approach in Spring and (slides 29 on) given an introductory example. The Mule code is more complex, but follows the same structure: org.mule.config.spring.handlers.MuleNamespaceHandler is the namespace handler; org.mule.config.spring.parsers.AbstractMuleBeanDefinitionParser and subclasses are the bean definition parsers.

  • A couple of blog posts (1, 2) that give a developer’s-eye overview.

3* Useful papers on mutable/extensible containers 1, 2