Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Custom Mule Components with a Custom Namespace

DZone's Guide to

Custom Mule Components with a Custom Namespace

· Integration Zone
Free Resource

Share, secure, distribute, control, and monetize your APIs with the platform built with performance, time-to-value, and growth in mind. Free 90-day trial of 3Scale by Red Hat

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 associated one more more XML namespace URIs with one or more namespace handlers.
  • 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 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 <mule> 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.

Explore the core elements of owning an API strategy and best practices for effective API programs. Download the API Owner's Manual, brought to you by 3Scale by Red Hat

Topics:

Published at DZone with permission of Ivan K. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}