Skip to Content

Introduction

Sometimes there is a requirement to send the synchronous response immediately without waiting to process the message in receiver side like this question SOAP to IDOC scenario in PI 7.31 ICO (send message id back to SOAP Client), previously we used ccBPM to achieve this requirement, with in the BPM open sync/async bridge and generate the response using mapping step and send it to sender then send the message asynchronously to the receiver.

This requirement is not possible to send the immediate sync response message to the sender even with sync/async bridge using adapter modules without BPM.

In this blog i want to show how we can achieve this using custom adapter module.

Design

Create below sender data type for web service request.

Create below sender data type for web service response.

Create below data type for receiver file.

Create sender service interface for web service synchronous request.

Create below service interface for receiver file.


Create below simple message mapping from sender web service request to receiver file structure.

Configuration

Create below IFlow for synchronous web service request to File. (SOAP to File).

Configure below custom adapter module in SOAP sender channel.

Configure file receiver channel normally.

The below is the custom adapter module SyncAsyncResponseBean module which is configured in SOAP sender channel. This adapter module change the synchronous request to asynchronous and send the message to messaging system asynchronously. After that generate the response message using below CreateWebserviceResponse class and send the response message to sender.

import java.rmi.RemoteException;

import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.transaction.Transaction;

import com.sap.aii.adapter.xi.ms.XIMessage;
import com.sap.aii.af.app.modules.orch.MessagingBeanBase;
import com.sap.aii.af.lib.mp.module.ModuleContext;
import com.sap.aii.af.lib.mp.module.ModuleData;
import com.sap.aii.af.lib.mp.module.ModuleException;
import com.sap.aii.af.service.cpa.CPAObjectType;
import com.sap.aii.af.service.cpa.Channel;
import com.sap.aii.af.service.cpa.LookupManager;
import com.sap.engine.interfaces.messaging.api.DeliverySemantics;
import com.sap.engine.interfaces.messaging.api.Message;
import com.sap.engine.interfaces.messaging.api.MessageClass;
import com.sap.engine.interfaces.messaging.api.XMLPayload;

public class SyncAsyncResponseBean extends MessagingBeanBase implements SessionBean {
	private static final long serialVersionUID = -5736277688013260902L;

	public ModuleData process(ModuleContext moduleContext, ModuleData moduleData) throws ModuleException {
		init();
		try {
			Channel channel = LookupManager.getInstance().getCPAObject(CPAObjectType.CHANNEL, moduleContext.getChannelID());
			Message reqMsg = (Message) moduleData.getPrincipalData();
			reqMsg.setDeliverySemantics(DeliverySemantics.ExactlyOnce);
			if ((this.txmanager != null) && (this.txmanager.getStatus() == 6)) {
				Transaction tx = (Transaction) moduleData.getSupplementalData("transaction");
				if (tx != null) {
					this.txmanager.resume(tx);
				}
			}
			retrieveConnection(moduleContext, channel).send(reqMsg);
			Message respMsg = this.afMessageFactory.createMessage(reqMsg.getToParty(), reqMsg.getFromParty(), reqMsg
					.getToService(), reqMsg.getFromService(), reqMsg.getAction());
			XMLPayload respPayload = respMsg.createXMLPayload();
			respPayload.setContent(new CreateWebserviceResponse().transform(reqMsg));
			respMsg.setDocument(respPayload);
			((XIMessage) respMsg).setMessageClass(MessageClass.APPLICATION_RESPONSE);
			respMsg.setDeliverySemantics(DeliverySemantics.BestEffort);
			respMsg.setRefToMessageId(reqMsg.getMessageId());
			moduleData.setPrincipalData(respMsg);
			return moduleData;
		} catch (Exception e) {
			throw new ModuleException(e.getMessage(), e);
		}
	}

	public void ejbActivate() throws EJBException, RemoteException {
	}

	public void ejbPassivate() throws EJBException, RemoteException {
	}

	public void ejbRemove() throws EJBException, RemoteException {
	}

	public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException {
	}
}

Code for CreateWebserviceResponse is below.

import java.io.ByteArrayOutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.sap.aii.af.lib.mp.module.ModuleException;
import com.sap.engine.interfaces.messaging.api.Message;

public class CreateWebserviceResponse {

	public byte[] transform(Message msg) throws ModuleException {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			factory.setIgnoringElementContentWhitespace(true);
			Document outputDoc = builder.newDocument();
			Element outMsgType = outputDoc.createElement("ns0:WebserviceResponse");
			outMsgType.setAttribute("xmlns:ns0", "http://mycompany.org");
			outputDoc.appendChild(outMsgType);
			Element messageIdElement = outputDoc.createElement("MessageId");
			messageIdElement.setTextContent(msg.getMessageId());
			outMsgType.appendChild(messageIdElement);
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();
			transformer.setOutputProperty("indent", "yes");
			DOMSource source = new DOMSource(outputDoc);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			StreamResult result = new StreamResult(baos);
			transformer.transform(source, result);
			return baos.toByteArray();
		} catch (Exception e) {
			throw new ModuleException(e.getMessage(), e);
		}
	}
}

Include these two references in the application-j2ee-engine.xml which you created in the EAR application, the module is used classes from these applications.

<reference 
		reference-type="hard">
		<reference-target 
			provider-name="sap.com" 
			target-type="service">com.sap.aii.adapter.xi.svc</reference-target>
	</reference>
	<reference 
		reference-type="hard">
		<reference-target 
			provider-name="sap.com" 
			target-type="application">com.sap.aii.af.app</reference-target>
</reference>

Testing

Send the web service request from SOAP UI and you can see below we got the response with message id which created in PI to the sender web service.

You can see below audit log which shows the message successfully sent to messaging system asynchronously and then send the response back to the sender.

The file successfully placed in the target folder.

We can see the file content what we expected after the mapping.

Conclusion

Using above custom adapter module we can send the synchronous response message  immediately without waiting for message processing in receiver side.  I hope this help to the community.

To report this post you need to login first.

3 Comments

You must be Logged on to comment or reply to a post.

  1. Evgeniy Kolmakov

    Hi Praveen!

    Thank you very much for sharing this! I had the same requirement and developed custom module too but your implementation seems to be more elegant and easy-to-use.
    One question: I can’t find definition of class “CreateWebserviceResponse” in your code. Or is it standard SAP class?

    Regards, Evgeniy.

    (0) 
    1. Praveen Gandepalli Post author

      Hi Evgeniy,

      Thanks for your positive response, i have updated the class CreateWebserviceResponse class in the blog. I just need to cater for dynamically call this class because each requirement we need to write separate class to generate the response.

      Regards,
      Praveen.

      (0) 

Leave a Reply