| 2 January 2010 |
Although there are a lot of evaluators available in Mule CE it is very easy to add your own evaluator. In my case we have a self defined message format that holds some properties in the header of a message (similar to JMS Message, MuleMessage, etc.). To get access to these properties in the Mule config I created a custom evaluator that made this possible. Although there will be other solutions available for this situation, I found this a nice (pragmatic) way to solve it. It also provides a base to start from in case of possible changes in the future.
The XML schema that describes our message looks like:
-
<?xml version="1.0" encoding="UTF-8"?>
-
<xs:schema xmlns:tns="http://www.redstream.nl/message/v01_0"
-
xmlns:xs="http://www.w3.org/2001/XMLSchema"
-
targetNamespace="http://www.redstream.nl/message/v01_0"
-
elementFormDefault="qualified">
-
<xs:element name="message" type="tns:MessageType"/>
-
<xs:complexType name="MessageHeaderType">
-
<xs:annotation>
-
<xs:documentation>The header for a message. Contains all metadata about the message.</xs:documentation>
-
</xs:annotation>
-
<xs:sequence>
-
<xs:element name="messageVersion" type="tns:versionNumberType" minOccurs="1" maxOccurs="1"/>
-
<xs:element name="PropertySet" type="tns:PropertySetType" minOccurs="0" maxOccurs="1"/>
-
</xs:sequence>
-
</xs:complexType>
-
<xs:complexType name="MessageType">
-
<xs:sequence>
-
<xs:element name="MessageHeader" type="tns:MessageHeaderType" minOccurs="1" maxOccurs="1"/>
-
<xs:element name="MessageBody" type="tns:MessageBodyType" minOccurs="0" maxOccurs="1"/>
-
</xs:sequence>
-
</xs:complexType>
-
<xs:complexType name="MessageBodyType">
-
<xs:sequence>
-
<xs:any minOccurs="0" maxOccurs="1"/>
-
</xs:sequence>
-
</xs:complexType>
-
<xs:complexType name="PropertyType">
-
<xs:attribute name="key" type="xs:string" use="required"/>
-
<xs:attribute name="value" type="xs:string"/>
-
</xs:complexType>
-
<xs:complexType name="PropertySetType">
-
<xs:annotation>
-
<xs:documentation>Set of properties</xs:documentation>
-
</xs:annotation>
-
<xs:sequence>
-
<xs:element name="Property" type="tns:PropertyType" minOccurs="0" maxOccurs="unbounded"/>
-
</xs:sequence>
-
</xs:complexType>
-
<xs:simpleType name="versionNumberType">
-
<xs:annotation>
-
<xs:documentation>Type used to define a Message versionNumber</xs:documentation>
-
</xs:annotation>
-
<xs:restriction base="xs:string">
-
<xs:pattern value="v[0-9]{2}_[0-9]{1}"/>
-
<xs:length value="5"/>
-
</xs:restriction>
-
</xs:simpleType>
-
</xs:schema>
And example message looks like this:
-
<?xml version="1.0" encoding="UTF-8"?>
-
<tns:message xsi:schemaLocation="http://www.redstream.nl/message/v01_0 message_v01_0.xsd" xmlns:tns="http://www.redstream.nl/message/v01_0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-
<tns:MessageHeader>
-
<tns:messageVersion>v01_0</tns:messageVersion>
-
<tns:PropertySet>
-
<tns:Property key="SOURCE_SYSTEM" value="CRM" />
-
<tns:Property key="ENTITY" value="Order" />
-
<tns:Property key="VERSION" value="v01_0" />
-
</tns:PropertySet>
-
</tns:MessageHeader>
-
<tns:MessageBody>
-
<payload-root>Blablabla</payload-root>
-
</tns:MessageBody>
-
</tns:message>
To get access to the 'Property' elements in the message-header via an 'evaluator expression' I created the following Java class based on the info given here:
-
package nl.redstream.evaluator;
-
-
import nl.redstream.cdm.util.JaxbUtil;
-
import java.util.List;
-
import javax.xml.bind.JAXBException;
-
import nl.redstream.message.v01_0.Message;
-
import nl.redstream.message.v01_0.PropertySetType;
-
import nl.redstream.message.v01_0.PropertyType;
-
import org.apache.log4j.Logger;
-
import org.mule.api.MuleMessage;
-
import org.mule.api.expression.ExpressionEvaluator;
-
import org.mule.api.expression.RequiredValueException;
-
import org.mule.config.i18n.CoreMessages;
-
-
/**
-
*
-
* @author pascal
-
*/
-
public class CdmMessageEvaluator implements ExpressionEvaluator {
-
-
-
protected static Logger logger = Logger.getLogger(CdmMessageEvaluator.class);
-
-
Object result = null;
-
-
boolean required;
-
-
//Is the header optional? the '*' denotes optional
-
if (expression.endsWith("*")) {
-
expression = expression.substring(expression.length() - 1);
-
required = false;
-
} else {
-
required = true;
-
}
-
-
Message cdmMsg = null;
-
//Look up the property on the message
-
if (msg.getPayload() instanceof Message)
-
{
-
cdmMsg = (Message) msg.getPayload();
-
{
-
try {
-
// Apparantly we are getting the message as an XML String
-
// So use jaxb to create a Object of it
-
cdmMsg =
-
JaxbUtil.unmarshal(Message.class,
-
msg.getPayload().toString());
-
} catch (JAXBException ex) {
-
}
-
-
} else {
-
// It's not an expected payload. Fail this operation.
-
throw new UnsupportedOperationException("Unexpected inputtype received: " + msg.getPayload().getClass().getName());
-
}
-
-
result = getProperty(cdmMsg.getMessageHeader().getPropertySet(),expression.toUpperCase());
-
-
if (result == null && required) {
-
throw new RequiredValueException(CoreMessages.expressionEvaluatorReturnedNull(NAME, expression));
-
}
-
return result;
-
}
-
-
return NAME;
-
}
-
-
}
-
-
{
-
List<PropertyType> props = propertySet.getProperties();
-
-
String value = null;
-
for (PropertyType prop: props) {
-
if (prop.getKey().toUpperCase().equals(property))
-
{
-
value = prop.getValue();
-
break;
-
}
-
}
-
return value;
-
}
-
}
I think the code is fairly straightforward. In the method 'evaluate' I receive the payload of the MuleMessage and I check to see if the payload is a JAXBObject of the type Message. If it is a String I assume I receive the XML message as payload and transform the String to the expected JAXB object myself (see this post for more details about this subject).
When I have access to the JAXBObject I simply question the properties of the message for the property name that is supplied with the expression. If a value is found this is returned by the expression.
To make use of this evaluator I 'bootstrapped' it with the Mule application. This can simply be done by adding the file 'registry-bootstrap.properties' to your classpath in the folder 'META-INF/services/org/mule/config' (as explained here). In this file I put:
-
object.1=nl.redstream.evaluator.CdmMessageEvaluator
And that's it. To make use of this evaluator I have in my Mule config:
-
<expression-recipient-list-router evaluator="custom" custom-evaluator="cdm-msg-property" expression="ADDR_LIST" transformer-refs="JaxbObjectToCdmXml">
This will take the property 'ADDR_LIST' out of my Message and use it as outbound Endpoint, but I will explain that in another post.


1 comment to 'Create your own Expression Evaluator in Mule'
12 January 2010
[...] by making use of my custom evaluator (The creation and use of the custom evaluator is described here. It also explains more about the used xml schema for my message). With this configuration in place [...]