Custom Mule Components with a Custom Namespace

By | February 14, 2014
Reading Time: 7 minutes

I want my Mule configuration files to look cool, even though I use components, transformers etc that I implemented myself. 🙂
In this article I will show a very simple example of how to create a custom Mule component and implement support for my own namespace in Mule configuration files.
Later, when I use my custom component in a Mule flow, it will look like this:

<ivan:icomponent ivanFlag="false">
    <ivan:ivanChildElement childElementAttribute="some string 123"/>
    <ivan:ivanChildElement childElementAttribute="some string 456"/>
</ivan:icomponent

Background

Mule configuration files are Spring configuration files with a number of custom namespaces. For each namespace that you want to add to Spring, you supply the following artifacts:

  • An XML schema.
    The XML schema defines the structure of the custom element(s). In this example I will add one single type of element.
  • A component definition parser.
    A parser that is responsible for extracting information from a DOM tree representation of a Mule (Spring) configuration file. This is actually not as difficult as it may sound, as we soon will see, since handling of common parser behaviour has already been implemented.
  • A namespace handler.
    The namespace handler connects the name of the custom element(s) with one or more component definition parser(s).
  • A file named “spring.handlers”.
    This file associates one or more XML namespace URIs with a number namespace handler(s).
  • A file named “spring.schemas”.
    This file associates one or more XML schema URLs with the location(s) of the XML schema(s) in the file system.

For additional background information on “Spring Extensible XML Authoring”, which is what Spring calls this, please refer to this web page.

Preparation

Using Eclipse or, better yet SpringSource Tool Suite, with the MuleStudio plug-in installed, create a new Mule project. When creating the project, create a new flow configuration file in the process.
I have used the Mule 3.4 community edition when I created this example but the Spring framework has had this feature since version 2.0 so older version should do as well.

XML Schema

We’ll start out with creating the XML schema for the custom Mule component. This file is named “ivan_v1.xsd” and is to be located in a directory named “META-INF” in src/main/resources in the example project.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.ivan.com/schema/mule/test"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:mule="http://www.mulesoft.org/schema/mule/core"
            targetNamespace="http://www.ivan.com/schema/mule/test"
            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/3.4/mule.xsd"/>

    <!--
        Defines the element of my custom component and provide some
        documentation for the component.
        In this example a custom component is created and the substitution-
        group used is thus abstract-component. There are a number of other
        substitution groups that can be used, for instance abstract-filter
        if you want to create a custom filter.
        Please refer to the Mule core XML schema (mule.xsd) for more
        information.
    -->
    <xsd:element name="icomponent" type="ivanComponentType"
        substitutionGroup="mule:abstract-component">
        <xsd:annotation>
            <xsd:documentation>
                Documentation of the Ivan component.
            </xsd:documentation>
        </xsd:annotation>
    </xsd:element>

    <!--
        Definition of the custom component type and some documentation.
    -->
    <xsd:complexType name="ivanComponentType">
        <xsd:complexContent>
            <xsd:extension base="mule:abstractComponentType">
                <xsd:sequence>
                    <!--
                        Define child elements of the custom component element
                        that are to be handled by the same component definition
                        parser.
                    -->
                    <xsd:element name="ivanChildElement" minOccurs="0" maxOccurs="2">
                        <xsd:annotation>
                            <xsd:documentation>
                                An optional child element of the Ivan component.
                            </xsd:documentation>
                        </xsd:annotation>
                        <xsd:complexType mixed="true">
                            <xsd:attribute name="childElementAttribute" type="xsd:string">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        An optional attribute of the child element.
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:attribute>
                        </xsd:complexType>
                    </xsd:element>
                </xsd:sequence>
                <!--
                    Define attributes of the custom component element type.
                    These are handled automatically by existing component
                    definition parser code and any values supplied in a Mule
                    configuration file will be injected into instances of
                    the custom component using a setter-method of the property
                    with the same name as the attribute.
                -->
                <xsd:attribute name="ivanFlag" type="xsd:boolean">
                    <xsd:annotation>
                        <xsd:documentation>
                            An optional attribute of the Ivan component. Default is true.
                        </xsd:documentation>
                    </xsd:annotation>
                </xsd:attribute>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:schema>

There are three main parts to this XML schema; first the imports of two XML schemas used when creating the XML schema. Then the XML element of the custom Mule component is defined. Finally, the type of the custom Mule component element is defined.
Note that the super-type of the custom Mule component you want to create needs to be specified when defining the custom component element, using the substitutionGroup attribute.

Spring Handlers File

The Spring handlers file is named “spring.handlers” and is located in the same “META-INF” directory as the XML schema we just created, namely in src/main/resources. In this example, the Spring handlers file consists of one single line:

http\://www.ivan.com/schema/mule/test=com.ivan.config.IvanNamespaceHandler

Again, this is the file that maps an XML namespace to a namespace handler class.

Spring Schemas File

The Spring schemas file, “spring.schemas”, is also located in the “META-INF” directory in src/main/resources and contains a mapping from a XML schema location URL to a local file. As with the previous file, this is also a one-liner:

http\://www.ivan.com/schema/mule/test/ivan.xsd=META-INF/ivan_v1.xsd

Custom Namespace Handler

The responsibilities of the custom namespace handler is to register a component definition parser for the name of the custom XML element and register the name of a child element to the custom element that is to be ignored. Actually, the child element of the custom element won’t be completely ignored, but it will be handled by the same component definition parser as the custom element, as we will see.

package com.ivan.config;

import org.mule.config.spring.handlers.AbstractMuleNamespaceHandler;

/**
 * Custom Spring namespace handler for Ivan's Mule extensions.
 * 
 * @see org.springframework.beans.factory.xml.NamespaceHandlerSupport
 * @author Ivan Krizsan
 */
public class IvanNamespaceHandler extends AbstractMuleNamespaceHandler {
    /* (non-Javadoc)
     * @see org.springframework.beans.factory.xml.NamespaceHandler#init()
     */
    @Override
    public void init() {
        /* Register a custom parser for the custom component. */
        registerBeanDefinitionParser("icomponent",
            new IvanComponentDefinitionParser());

        /*
         * Ignore child element(s) of the <icomponent> element that are
         * handled by the above component definition parser.
         */
        registerIgnoredElement("ivanChildElement");
    }
}

Component Definition Parser

The component definition parser is to perform any custom parsing of a DOM representation of Mule configuration files.
I must confess to having added the attribute of the <icomponent> element as well as its child element just to make this example more interesting and not too simplistic.

package com.ivan.config;

import org.mule.config.spring.parsers.specific.SimpleComponentDefinitionParser;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.ivan.component.IvansComponent;

/**
 * Spring bean definition parser for the Ivan namespace.
 *
 * @author Ivan Krizsan
 * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser
 */
public class IvanComponentDefinitionParser extends SimpleComponentDefinitionParser {
    /**
     * Default constructor.
     */
    public IvanComponentDefinitionParser() {
        super(null, IvansComponent.class);
    }

    /**
     * Handles any child elements of the element(s) handled by this component
     * definition parser and not by a separate component definition parser.
     * 
     * @param inParentElement Parent element which child elements to handle.
     * @param inParserContext Spring bean definition parser context.
     * @param inBeanDefinitionBuilder Spring bean definition builder.
     */
    @Override
    protected void parseChild(final Element inParentElement,
        final ParserContext inParserContext,
        final BeanDefinitionBuilder inBeanDefinitionBuilder) {
        System.out
            .println("***** Ivan component definition parser to handle child elements of: " +
                inParentElement.getLocalName());

        /* List the names of child nodes. */
        final NodeList theChildNodes = inParentElement.getChildNodes();
        for (int i = 0; i < theChildNodes.getLength(); i++) {
            System.out.println("      Child node: " +
                theChildNodes.item(i).getLocalName());
        }

        /*
         * Must call the superclass method in order for, for instance,
         * attributes on the component element to be handled correctly.
         * If this call is omitted in this example, then the value of the
         * attribute "ivanFlag" will not be set.
         */
        super.parseChild(inParentElement, inParserContext,
            inBeanDefinitionBuilder);
    }
}

We can see that:

  • In the constructor, the superclass’ constructor is invoked with two arguments.
    The first argument is not used in the superclass, so it is set to null. The second argument is the custom component class that we shortly will implement.
  • Since we pretend that we want to handle child elements of the <icomponent> element ourselves, we need to override the parseChild method.
  • When the parseChild method is invoked, the inParentElement argument contains a reference to an Element instance representing the <icomponent> element.
    There will be solid proof for this in the console when we run the program.
  • If the parseChild method is overridden, it is important to call the superclass’ method at some point, if we want for instance values of attributes on the <icomponent> element to be injected into the custom component instance properly.

Custom Component Class

The custom component class need no changes just because we have a custom XML element in the Mule configuration file represent the component.

package com.ivan.component;

import java.util.Date;
import org.mule.api.MuleEventContext;
import org.mule.api.MuleMessage;
import org.mule.api.lifecycle.Callable;

/**
 * Implementation of the Ivan component.
 * This component just prints some messages to the console when receiving
 * a message.
 *
 * @author Ivan Krizsan
 */
public class IvansComponent implements Callable {
    /* Instance variable(s): */
    /**
     * A flag that can be set on the component. 
     * Set any default value here or in the constructor.
     */
    protected boolean mIvanFlag = true;

    /* (non-Javadoc)
     * @see org.mule.api.lifecycle.Callable#onCall(org.mule.api.MuleEventContext)
     */
    @Override
    public Object onCall(final MuleEventContext inEventContext)
        throws Exception {
        final MuleMessage theReceivedMessage = inEventContext.getMessage();

        final Date theCurrentDate = new Date();
        System.out.println("***** Ivan's component received a message at " +
            theCurrentDate);
        System.out.println("      Payload of received message is:");
        System.out.println(inEventContext.getMessageAsString());
        System.out.println("*****");

        return theReceivedMessage;
    }

    /**
     * Sets the value of the attribute "ivanFlag" in the component element.
     * Spring takes care of injecting values of any attributes present on the
     * component element.
     * A getter-method for attribute values like this is not required.
     * 
     * @param inIvanFlag Value of attribute "ivanFlag".
     */
    public void setIvanFlag(final boolean inIvanFlag) {
        mIvanFlag = inIvanFlag;
        System.out.println("***** Setting ivanFlag on Ivan's component: " +
            inIvanFlag);
    }
}

Mule Configuration File

With all the above preparation done, I am going to write a Mule configuration file with my new, shiny, custom Mule component. I really hope it will look as good as Mule’s own components:

<?xml version="1.0" encoding="UTF-8"?>
<mule
    xmlns:file="http://www.mulesoft.org/schema/mule/file"
    xmlns:ivan="http://www.ivan.com/schema/mule/test"
    xmlns="http://www.mulesoft.org/schema/mule/core"
    xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
    xmlns:spring="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="CE-3.4.0"
    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 http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd
http://www.ivan.com/schema/mule/test http://www.ivan.com/schema/mule/test/ivan.xsd">

    <file:connector
        name="NonStreamingFileConnector"
        autoDelete="true"
        streaming="false"
        validateConnections="true" doc:name="File"/>
    
    <flow name="MuleCustomComponentFlow1" doc:name="MuleCustomComponentFlow1">
        <file:inbound-endpoint path="inbox" connector-ref="NonStreamingFileConnector" doc:name="File"/>
        
        <ivan:icomponent ivanFlag="false">
            <ivan:ivanChildElement childElementAttribute="some string 123"/>
            <ivan:ivanChildElement childElementAttribute="some string 456"/>
        </ivan:icomponent>
    </flow>
</mule>

Note that:

  • In the element there is a XML namespace declaration connecting the namespace prefix “ivan” with the namespace URI we have seen in, among other places, the XML schema written earlier.
  • Last in the schemaLocation attribute on the <mule> element, a XML schema location for the namespace URI discussed above is specified.
    The destination URL looks like a perfectly normal URL while, at least I am pretty certain that this XML schema is not present at that URL. However, recall the “spring.schemas” file created earlier, which redirects search for the XML schema to a local file.
  • In the Mule flow, after the file inbound endpoint, my new custom Mule component appears!
    ….and it is looking good! 🙂

Run the Example Program

In Eclipse (or the IDE of your choice), start the example program by right-clicking the project and selecting “Run As -> Mule Application”. Refresh the project in Eclipse and note that a directory “inbox” should appear at root level of the project. Copy a file into this directory and observe the console output:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Initializing app 'mulecustomcomponent'                   +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
...
***** Ivan component definition parser to handle child elements of: icomponent
      Child node: null
      Child node: ivanChildElement
      Child node: null
      Child node: ivanChildElement
      Child node: null
...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Starting app 'mulecustomcomponent'                       +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
...
***** Setting ivanFlag on Ivan's component: false
...
***** Ivan's component received a message at Fri Feb 14 06:39:08 CET 2014
      Payload of received message is:
http://www.ivan.com/schema/mule/test=com.ivan.config.IvanNamespaceHandler

*****

We can see that, as part of the initialization process, our custom component definition parser is invoked for the <icomponent> element and, as expected, discovers the two child elements.
Later, when starting the Mule application up, the value false, as specified in the Mule configuration file, is injected into the ivanFlag property of the custom component instance.
Finally, when the custom component receives a message, it logs the time and the message payload to the console.

4 thoughts on “Custom Mule Components with a Custom Namespace

  1. luico

    As would do to get the values ​​ivanchildren in IvanComponent ?

    Reply
    1. Ivan Krizsan Post author

      Hi!
      The IvanComponentDefinitionParser class needs to be changed as in the listing below. In addition, you need to add the appropriate instance variable and setter-method to the IvanComponent class (which is not shown here). Hope this helps!
      package com.ivan.config;

      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      import org.mule.api.lifecycle.Disposable;
      import org.mule.api.lifecycle.Initialisable;
      import org.mule.config.spring.parsers.specific.SimpleComponentDefinitionParser;
      import org.mule.object.AbstractObjectFactory;
      import org.mule.object.SingletonObjectFactory;
      import org.springframework.beans.factory.support.AbstractBeanDefinition;
      import org.springframework.beans.factory.support.BeanDefinitionBuilder;
      import org.springframework.beans.factory.support.GenericBeanDefinition;
      import org.springframework.beans.factory.xml.ParserContext;
      import org.w3c.dom.Element;
      import org.w3c.dom.NamedNodeMap;
      import org.w3c.dom.Node;
      import org.w3c.dom.NodeList;
      import com.ivan.component.IvansComponent;

      /**
      * Spring bean definition parser for the Ivan namespace.
      *
      * @author Ivan Krizsan
      * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser
      */
      public class IvanComponentDefinitionParser extends
      SimpleComponentDefinitionParser {
      /* Copied from superclass and made protected, as to be available to this class and subclasses. */
      @SuppressWarnings(“rawtypes”)
      protected static Class OBJECT_FACTORY_TYPE = SingletonObjectFactory.class;

      /*
      * These two instance variables are replacements for the corresponding private
      * instance variables in the superclass.
      */
      protected Map mBeanProperties =
      new HashMap();
      @SuppressWarnings(“rawtypes”)
      protected Class mComponentInstanceClass;

      /**
      * Default constructor.
      */
      public IvanComponentDefinitionParser() {
      super(null, IvansComponent.class);
      mComponentInstanceClass = IvansComponent.class;
      }

      /**
      * Handles any child elements of the element(s) handled by this component
      * definition parser and not by a separate component definition parser.
      *
      * @param inParentElement Parent element which child elements to handle.
      * @param inParserContext Spring bean definition parser context.
      * @param inBeanDefinitionBuilder Spring bean definition builder.
      */
      @Override
      protected void parseChild(final Element inParentElement,
      final ParserContext inParserContext,
      final BeanDefinitionBuilder inBeanDefinitionBuilder) {
      System.out
      .println(“***** Ivan component definition parser to handle child elements of: ”
      + inParentElement.getLocalName());

      /*
      * Must call the superclass method in order for, for instance,
      * attributes on the component element to be handled correctly.
      * If this call is omitted in this example, then the value of the
      * attribute “ivanFlag” will not be set.
      */
      super.parseChild(inParentElement, inParserContext,
      inBeanDefinitionBuilder);

      /* List the names of child nodes. */
      final NodeList theChildNodes = inParentElement.getChildNodes();
      for (int i = 0; i < theChildNodes.getLength(); i++) {
      System.out.println(" Child node: "
      + theChildNodes.item(i).getLocalName());
      }

      /*
      * List the contents of the element nodes and see to that a list
      * of the nodes contents is injected into the instance of IvanComponent that will be
      * created.
      */
      final List theChildNodeContentsList = new ArrayList();
      final NodeList theIvanChildElementsNodes =
      inParentElement.getElementsByTagNameNS(
      “http://www.ivan.com/schema/mule/test”, “ivanChildElement”);
      for (int i = 0; i < theIvanChildElementsNodes.getLength(); i++) {
      final Node theIvanChildElementNode =
      theIvanChildElementsNodes.item(i);

      final String theChildNodeContents =
      theIvanChildElementNode.getTextContent();
      System.out.println(" node ” + (i + 1)
      + ” element contents: ” + theChildNodeContents);
      theChildNodeContentsList.add(theChildNodeContents);
      }
      mBeanProperties.put(“ivanChildElementContents”,
      theChildNodeContentsList);
      }

      /*
      * Overridden to use the accessible instance variables, instead of the private ones.
      */
      @Override
      protected void preProcess(final Element element) {
      super.preProcess(element);

      NamedNodeMap attrs = element.getAttributes();

      int numAttrs = attrs.getLength();
      Node attr;
      for (int i = numAttrs – 1; i >= 0; –i) {
      attr = attrs.item(i);
      if (attr.getNamespaceURI() == null) {
      mBeanProperties.put(attr.getNodeName(), attr.getNodeValue());
      attrs.removeNamedItem(attr.getNodeName());
      }
      }
      }

      /*
      * Overridden to use the accessible instance variables, instead of the private ones.
      */
      @Override
      protected AbstractBeanDefinition getObjectFactoryDefinition(
      final Element element) {
      AbstractBeanDefinition objectFactoryBeanDefinition =
      new GenericBeanDefinition();
      objectFactoryBeanDefinition.setBeanClass(OBJECT_FACTORY_TYPE);
      objectFactoryBeanDefinition.getPropertyValues().addPropertyValue(
      AbstractObjectFactory.ATTRIBUTE_OBJECT_CLASS,
      mComponentInstanceClass);
      objectFactoryBeanDefinition.getPropertyValues().addPropertyValue(
      “properties”, mBeanProperties);

      objectFactoryBeanDefinition.setInitMethodName(Initialisable.PHASE_NAME);
      objectFactoryBeanDefinition.setDestroyMethodName(Disposable.PHASE_NAME);
      return objectFactoryBeanDefinition;
      }
      }

      Reply
  2. zzz

    I have custom xml-namespace, but for some reason, it doesn’t work like i wanted. Whenever i used my tags multiple times in one flow, only the last attributes is stored to my component.

    Reply
    1. Ivan Krizsan Post author

      Hi!
      Well, it is Spring beans and the default scope for Spring beans is the singleton scope, in which there will be at most one instance of any given bean. With that said, having thought about development with the Mule ESB for some time after having written this article and I would recommend minimizing the amount of XML in your integration(s).
      Instead I would recommend using plain Spring, with Java configuration, to the largest extent possible and only use Mule to manage the connections with the external systems.
      The Mule way of programming in XML, albeit hidden behind a graphical editor, does not promote good, maintainable, code. Even Spring is moving away from XML configuration and put more emphazis on Java configuration these days.
      Happy coding!

      Reply

Leave a Reply to Ivan Krizsan Cancel reply

Your email address will not be published. Required fields are marked *