There are some situations in our deployment scenarios, which it is required to do a custom transformation of incoming messages. This custom tasks are sometimes can not be achieved with the inbuilt mediators in WSO2 Enterprise Service Bus. So , in those kind of scenarios , we ll have to write a custom mediator to achieve this. With this post , I am explaining how to write a custom mediator for WSO2 ESB.
Writing a custom mediator for ESB , means we are writing a class meditor for ESB which contains the required logic in the mediate() method of the class. When writing a class mediator, there is a basic step follow to be compatible to deploy in WSO2 ESB. We need to extend our mediator from the abstract class "org.apache.synapse.mediators.AbstractMediator". In order to do that , you will have to add a dependency to your project for synapse-core as follows;
<dependencies>
<dependency>
<groupId>org.apache.synapse</groupId>
<artifactId>synapse-core</artifactId>
<version>${synpase.core.version}</version>
</dependency>
</dependencies>
Writing a custom mediator for ESB , means we are writing a class meditor for ESB which contains the required logic in the mediate() method of the class. When writing a class mediator, there is a basic step follow to be compatible to deploy in WSO2 ESB. We need to extend our mediator from the abstract class "org.apache.synapse.mediators.AbstractMediator". In order to do that , you will have to add a dependency to your project for synapse-core as follows;
<dependencies>
<dependency>
<groupId>org.apache.synapse</groupId>
<artifactId>synapse-core</artifactId>
<version>${synpase.core.version}</version>
</dependency>
</dependencies>
Before digging in to the code it self, let me explain , what i am going to achieve with this custom mediator.
I have following incoming message ;
<dat:addJobWorkLocation xmlns:dat="http://ws.wso2.org/dataservice">
<dat:job_id>2037826</dat:job_id>
<dat:preferred_city_code>,CMB,GM,KND,</dat:preferred_city_code>
<dat:country_code>SL</dat:country_code>
</dat:addJobWorkLocation>
From this incoming message , i need to generate messages with the same type by splitting the comma separated string passed with the local name "preferred city code". After generating , it should be like follows,
<dat:addJobWorkLocation xmlns:dat="http://ws.wso2.org/dataservice">
<dat:job_id>2037826</dat:job_id>
<dat:preferred_city_code>CMB</dat:preferred_city_code>
<dat:country_code>SL</dat:country_code>
</dat:addJobWorkLocation>
<dat:addJobWorkLocation xmlns:dat="http://ws.wso2.org/dataservice">
<dat:job_id>2037826</dat:job_id>
<dat:preferred_city_code>GM</dat:preferred_city_code>
<dat:country_code>SL</dat:country_code>
</dat:addJobWorkLocation>
<dat:addJobWorkLocation xmlns:dat="http://ws.wso2.org/dataservice">
<dat:job_id>2037826</dat:job_id>
<dat:preferred_city_code>KND</dat:preferred_city_code>
<dat:country_code>SL</dat:country_code>
</dat:addJobWorkLocation>
Since we do not have an inbuilt mediator to fulfill this task, i am writing a custom mediator achieve that.
Name of my custom mediator is "SmartSplitMediator" and i am extending it from the AbstractMediator as follows.
public class SmartSplitMediator extends AbstractMediator {
...
}
Once i extended my class with AbstractMediator, it is essential to implement the methods in the AbstractMediator class. So , i am implementing the method mediate in this class as follows.
public class SmartSplitMediator extends AbstractMediator {
...
public boolean mediate(MessageContext messageContext) {
...
...
}
...
public boolean mediate(MessageContext messageContext) {
...
}
...
}
Then i need to write my logic inside this mediate method. To achieve my target , i need to pass some variables in this class. So, for that task , i need to define those variables as class variables and add getters and setters for those variables. For this particular case, i need following variables.
- parentLocalName
- parentNamespace
- parentNamespacePrefix
- IteratingElementLocalName
- variableStringLocalName
- constantStringsLocalNames
I am adding these variables to my class as follows;
public class SmartSplitMediator extends AbstractMediator {
private String parentLocalName;
private String parentNamespace;
private String parentNamespacePrefix;
private String IteratingElementLocalName;
private String variableStringLocalName;
private String constantStringsLocalNames;
public boolean mediate(MessageContext messageContext) {
...
}
public String getParentLocalName() {
return parentLocalName;
}
public void setParentLocalName(String parentLocalName) {
this.parentLocalName = parentLocalName;
}
public String getParentNamespace() {
return parentNamespace;
}
public void setParentNamespace(String parentNamespace) {
this.parentNamespace = parentNamespace;
}
public String getParentNamespacePrefix() {
return parentNamespacePrefix;
}
public void setParentNamespacePrefix(String parentNamespacePrefix) {
this.parentNamespacePrefix = parentNamespacePrefix;
}
public String getIteratingElementLocalName() {
return IteratingElementLocalName;
}
public void setIteratingElementLocalName(String iteratingElementLocalName) {
IteratingElementLocalName = iteratingElementLocalName;
}
public String getVariableStringLocalName() {
return variableStringLocalName;
}
public void setVariableStringLocalName(String variableStringLocalName) {
this.variableStringLocalName = variableStringLocalName;
}
public String getConstantStringsLocalNames() {
return constantStringsLocalNames;
}
public void setConstantStringsLocalNames(String constantStringsLocalNames) {
this.constantStringsLocalNames = constantStringsLocalNames;
}
}
Then the remaining part is to write the logic inside the mediate method. With the above passed in variables i have the following logic inside my mediate method.
public boolean mediate(MessageContext messageContext) {
SOAPFactory fac = OMAbstractFactory.getSOAP11Factory();
SOAPBody soapBody = messageContext.getEnvelope().getBody();
OMElement parentElement = (OMElement) soapBody.getFirstElement();
String variableString = parentElement.getFirstChildWithName(new QName(getParentNamespace(),getVariableStringLocalName(),getParentNamespacePrefix())).getText();
//Here we are tokenizing the incoming string from commas
StringTokenizer variableTokenizer = new StringTokenizer(variableString,",");
OMElement newParentElement = fac.createOMElement(new QName(getParentNamespace(), getParentLocalName(), getParentNamespacePrefix()));
while (variableTokenizer.hasMoreTokens()) {
String variableValue = variableTokenizer.nextToken();
if (variableValue.length() > 0) {
OMElement secondLevelElement = fac.createOMElement(new QName(getParentNamespace(), getIteratingElementLocalName(), getParentNamespacePrefix()));
StringTokenizer tokenizer = new StringTokenizer(getConstantStringsLocalNames(), ",");
while (tokenizer.hasMoreTokens()) {
String constantLocalName = tokenizer.nextToken();
OMElement jobIdElement;
if (constantLocalName.equalsIgnoreCase(getVariableStringLocalName())) {
jobIdElement = fac.createOMElement(new QName(getParentNamespace(), constantLocalName, getParentNamespacePrefix()));
jobIdElement.setText(variableValue);
} else {
jobIdElement = fac.createOMElement(new QName(getParentNamespace(), constantLocalName, getParentNamespacePrefix()));
jobIdElement.setText(parentElement.getFirstChildWithName(new QName(getParentNamespace(),constantLocalName,getParentNamespacePrefix())).getText());
}
secondLevelElement.addChild(jobIdElement);
}
newParentElement.addChild(secondLevelElement);
}
}
//Now we have completed the preparation of the new body, We need to detach the existing body and attach the new body. We are doing it here.
SOAPBody body = messageContext.getEnvelope().getBody();
if (body.getFirstElement() != null) {
body.getFirstElement().detach();
}
body.addChild(newParentElement);
return true;
}
After completing the logic in the above method we are done in implementation. Now we need to compile this mediator and deploy it in the wso2esb-4.x.x/repository components/lib directory to be used in any of the sequences.
Once you deploy it in the above directory , you can use it as follows in any of the sequence;
<inSequence>
<log level="full"/>
<class name="com.js.mediator.split.SmartSplitMediator">
<property name="variableStringLocalName" value="preferred_city_code"/>
<property name="parentLocalName" value="WorkAuthorization"/>
<property name="constantStringsLocalNames" value="preferred_city_code,
country_code"/>
<property name="parentNamespacePrefix" value="dat"/>
<property name="parentNamespace" value="http://ws.wso2.org/dataservice"/>
<property name="IteratingElementLocalName" value="addJobWorkLocation"/>
</class>
<log level="full"/>
<iterate xmlns:dat="http://ws.wso2.org/dataservice"
id="foo"
expression="//dat:WorkAuthorization/dat:addJobWorkLocation"
sequential="true">
<target>
<sequence>
<log level="full"/>
<drop/>
</sequence>
</target>
</iterate>
</inSequence>
Here i am attaching following items with related to this blog post.
Helpful. Thanks.
ReplyDelete