One of the main elements of WSO2 Enterprise Integrator is the proxy services, which act as partners between an external system that makes the call (client) and the final service to which the call is destined (backend).
In this way, by placing ourselves in the middle of both the request and the response, we are able to carry out various operations (transformation, audit, security, etc) without having to modify the target service
Proxy services can be of multiple types, depending on how we define them through their ‘transport’ attribute.
JMS Consumer and JMS Publisher
The proxy services that we define in its ‘transport’ attribute as JMS (Java Message Service), will be what we consider a JMS Consumer. That is to say, a proxy service that will listen to the messages that arrive at a certain queue.
In this way we will be able to carry out an orchestration whereby an external system sends messages to a queue and the Enterprise Integrator will be aware of the messages it receives to forward them to a final service. This is a common way of performing system integrations in an asynchronous manner.
However, if instead of receiving a message from a queue, we were sending a message to a queue, we would be talking about a JMS Publisher.
But what happens if after reading from a queue, an error occurs when attempting to call the final service? How does the Enterprise Integrator handle these messages? There are different solutions for this and in this article we will deal with some of them.
JMS Transactions
How do they work? We will configure the Enterprise Integrator to allow the use of transactions associated with the management of JMS messages. This will ensure that, if after sending a JMS message the response is wrong, it will not be lost but will instead be redirected to another queue. This offers us the possibility to resend said message.
These transactions can be local, meaning only a single queue is involved. Alternatively they can be distributed, which means different queues intervene and the message is sent to all of them.
Dead Letter Channel
When we mentioned an intermediate queue, we were actually referring to the ‘Dead Letter Channel‘ queue. This creation queue, which is automated by the message manager, will function as a repository for those messages that due to external circumstances could not be delivered correctly.
Once we have this queue, we will be able to:
- Delete messages
- Retry sending
- Redirect them to another service.
Local Transactions with Java Message Service Consumer (JMS Consumer)
In order to see how it works, let’s look at a simple example. Here we will create a proxy service to act as JMS Consumer. It will receive a message through a queue and send it to a final service. We will use the WSO2 Message Broker as a message and queue manager. This also enables us to see how the wrong messages are sent to the Dead Letter Channel queue.
Configuration
First we must enable the transaction by modifying the EI_HOME/conf/axis2/axis2.xml file and including the parameter transport.jms.SessionTransacted to true for each configured connection factory:
<transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">
<parameter name="myQueueConnectionFactory" locked="false">
<parameter name="java.naming.factory.initial" locked="false">org.wso2.andes.jndi.PropertiesFileInitialContextFactory</parameter>
<parameter name="java.naming.provider.url" locked="false">conf/jndi.properties</parameter>
<parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">QueueConnectionFactory</parameter>
<parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
<parameter name="transport.jms.SessionTransacted">true</parameter>
</parameter>
</transportReceiver>
Design
We’re going to create a simple proxy service, known as a backend, which always works. This service has the following characteristics:
- It uses a mediator call which blocks the property set to true. By doing so, we maintain the same thread and the transaction can be carried out successfully.
- It uses the ‘SET_ROLLBACK_ONLY’ property in the fault sequence with the value set to true. This enables us to indicate that the transaction must be reversed and therefore the message will be sent to the Dead Letter Channel.
- It uses the ‘transport.jms.ContentType’ property to indicate the type of message that will arrive in the queue.
<proxy xmlns="http://ws.apache.org/ns/synapse" name="LocalTransactionJMSConsumerProxy"
startOnLoad="true" statistics="disable" trace="disable" transports="jms">
<target>
<inSequence>
<call blocking="true">
<endpoint>
<http method="POST" uri-template="http://localhost:8280/services/echo.echoHttpSoap12Endpoint"/>
</endpoint>
</call>
<log level="custom">
<property name="Transaction Action" value="Committed"/>
<property expression="$body" name="response"/>
</log>
</inSequence>
<faultSequence>
<property name="SET_ROLLBACK_ONLY" scope="axis2" value="true"/>
<log level="custom">
<property name="Transaction Action" value="Rollbacked"/>
</log>
</faultSequence>
</target>
<parameter name="transport.jms.ContentType">
<rules xmlns="">
<jmsProperty>contentType</jmsProperty>
<default>application/soap+xml</default>
</rules>
</parameter>
<description/>
</proxy>
Example
In order to ensure its correct functioning, we will go into the Message Broker console and publish a message in the JMSTransactionProxy queue (this queue would have been created automatically when the proxy service was created) and we will see in the log how the message ‘Committed’ and the body of the response are shown.
If we want to see how the rollback function works we can try sending a badly formed XML message. Then when we publish the message, the logs will show the error of the badly formed XML, the Rollbacked message and how said message is shown in the Dead Letter Channel. The message identifier can also be seen in the Message Broker log.
Dead Letter Channel Integration Pattern
The Dead Letter Channel is not a unique WSO2 solution but is a pattern of integration in itself. Therefore it is applicable via any message and queue manager.
Therefore, based on this pattern, we can manually implement it ourselves and thereby avoid the loss of messages. This can be carried out without the need for JMS transactions (Java Message Service).
In order to do this, we will simply add a message store mediator in the error sequence in which we will indicate the queue to which we want to redirect the messages which fail. This gives us the possibility to have different queues that we will use as Dead Letter Channels for various integrations.
<faultSequence> <log level="custom"> <property name="Transaction Status" value="Not Delivery"/> </log> <store messageStore="customDLCMessageStore" /> </faultSequence>
Redelivery Policy
We can also force the queue manager to take into account a redelivery policy when sending the message.
This configuration differs depending on the queue manager we are going to use.
We will configure the properties in different places for the message broker. First, we will configure the maximum number of attempts to be made in the file EI_HOME/wso2/broker/conf/broker.xml, modifying the variable maximumRedeliveryAttempts.
< maximumRedeliveryAttempts >2</ maximumRedeliveryAttempts > |
We will configure the maximum waiting time between attempts via the EI_HOME/conf/jndi.properties and the property redeliveryDelay file
amqp: //admin:admin@clientID/carbon?redeliverydelay='15000'&brokerlist='tcp://localhost:5673' |
Conclusion
We have seen how we can maintain integrity in the sending of messages with WSO2, and how it provides us with methods to do so easily. In a simple way we are able to avoid the loss of messages in our integrations.
In addition to the resources indicated here, based on the JMS (Java Message Service) protocol or on architecture patterns, we must also take into account that WSO2 provides us with two mediators. Message Store and Message Processor can help us a great deal when it comes to increasing the integrity of our queuing operations.