Thursday, 11 February 2010

In-Out to In-Only MEP transformation in ServiceMix ESB's bean component

Recently I needed to write servicemix-bean component that implements following scenario:
  • receives In-Out MEP request
  • sends response to received In-Out request
  • constructs In-Only MEP request and forwards it to another service
I had some problems to get it working and could not find help in the internet. So, when I finally solved that problem I decided to share my solution.

My component:
package org.maciekm;

import java.io.StringReader;
import java.util.Date;

import javax.annotation.Resource;
import javax.jbi.component.ComponentContext;
import javax.jbi.messaging.DeliveryChannel;
import javax.jbi.messaging.ExchangeStatus;
import javax.jbi.messaging.InOnly;
import javax.jbi.messaging.MessageExchange;
import javax.jbi.messaging.MessageExchangeFactory;
import javax.jbi.messaging.MessagingException;
import javax.jbi.messaging.NormalizedMessage;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;

import org.apache.servicemix.MessageExchangeListener;
import org.apache.servicemix.client.ServiceMixClient;
import org.apache.servicemix.client.ServiceMixClientFacade;
import org.apache.servicemix.jbi.jaxp.SourceTransformer;

public class MyService implements MessageExchangeListener {

@Resource
private DeliveryChannel channel;

@Resource
private ComponentContext context;

private String targetServiceNS;
private String targetServiceName;

public String getTargetServiceNS() {
return targetServiceNS;
}

public void setTargetServiceNS(String targetServiceNS) {
this.targetServiceNS = targetServiceNS;
}

public String getTargetServiceName() {
return targetServiceName;
}

public void setTargetServiceName(String targetServiceName) {
this.targetServiceName = targetServiceName;
}

public void onMessageExchange(MessageExchange exchange) throws MessagingException {
if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
try {
NormalizedMessage message = exchange.getMessage("in");
Source content = message.getContent();

// getting "in" message of In-Out request
String body = (new SourceTransformer()).toString(content);

// some your custom logic

// sending response to initial In-Out request - place some xml there
String out=".......... some xml ..............";
StreamSource respSource = new StreamSource(new StringReader(out));
message.setContent(respSource);
exchange.setMessage(message, "out");
channel.send(exchange);

// some business logic to prepare message to forward based on original request

// sending In-Only message to service defined by targetServiceNS and
// targetServiceName properties
String forwardOut=".......... some xml ..............";
StreamSource forwardSource = new StreamSource(new StringReader(forwardOut));
MessageExchangeFactory mef =
channel.createExchangeFactoryForService(new QName(targetServiceNS, targetServiceName));
InOnly inOnlyExch = mef.createInOnlyExchange();
NormalizedMessage forwardMessage = inOnlyExch.createMessage();
forwardMessage.setContent(forwardSource);
inOnlyExch.setInMessage(forwardMessage);
ServiceMixClient smc = new ServiceMixClientFacade(context);
smc.send(inOnlyExch);
} catch (TransformerException te) {
// please do it better way in real project ;)
throw new MessagingException(te);
}
}
}
}
Declaration in xbean.xml file:
<bean id="myService" class="org.maciekm.MyService">
<property name="targetServiceNS" value="http://maciekm.org/esb" />
<property name="targetServiceName" value="targetService" />
</bean>

Injected properties are defining target service of In-Only message. Hope it helps some of you:)