Custom Mule Components with a Custom Namespace

By | February 14, 2014

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:

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.

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:

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:

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.

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.

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.

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:

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:

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. 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
  1. 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

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