<?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/current/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>
Creating a Custom XML Namespace
Mule runtime engine version 3.8 reached its End of Life on November 16, 2021. For more information, contact your Customer Success Manager to determine how to migrate to the latest Mule version. |
XML schema definitions are used for each module to define the objects and properties that the module makes available to Mule Runtime. 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
:
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:
<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>
.<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
:
<xsd:import namespace="http://www.mulesoft.org/schema/mule/core/current"/>
<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 |
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:
<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:
http://www.mulesoft.org/schema/mule/core/current
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 current
, which Mule translates into the correct version.
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
http://www.mulesoft.org/schema/mule/tcp/3.8/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
http://www.mulesoft.org/schema/mule/tcp/3.8=org.mule.transport.tcp.config.TcpNamespaceHandler
The TcpNamespaceHandler
code is very simple because there is a base support class provided:
TcpNamespaceHandler.java
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. Note that Mule 3.7 and later uses Spring 4.1.6.
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 |
---|---|
org.mule.config.spring.parsers.generic.OrphanDefinitionParser |
Constructs 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. |
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. |
|
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. |
|
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. |
|
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 |
org.mule.config.spring.parsers.AbstractMuleBeanDefinitionParser |
This parser extends the Spring provided
|
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 havegeneric
,specific
, andcollection
, 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:
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.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.mulesoft.org/schema/mule/ssl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:mule="http://www.mulesoft.org/schema/mule/core/"
xmlns:tcp="http://www.mulesoft.org/schema/mule/tcp/"
targetNamespace="http://www.mulesoft.org/schema/mule/ssl/"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.mulesoft.org/schema/mule/core/"
schemaLocation="http://www.mulesoft.org/schema/mule/core/current/mule.xsd" />
<xsd:import namespace="http://www.mulesoft.org/schema/mule/tcp/"
schemaLocation="http://www.mulesoft.org/schema/mule/tcp/current/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.
-
Write a test case for the connector.
-
Use IDE’s auto completion to test each public getter (as a first approximation to the public API - tidy by hand).
-
Set the test value to something other than the default.
-
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). -
Write the schema definition (tweaking until the XML connector config shows no errors) (
META-INF/mule-foo.xsd
). -
Write the namespace handler (and any needed definition parsers) (
src/main/java/org/mule/providers/foo/config/FooNamespaceHandler
) -
Set the Spring handler mapping (
META-INF/spring.handlers
). -
Set the local schema mapping (
META-INF/spring.schemas
). -
Make sure the test runs.
-
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.
See Also
-
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. -
Useful papers on mutable and extensible containers: VariableContentContainers and ExtensibleContentModels