/* * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.carbon.transports.sap; import com.sap.conn.idoc.IDocDocumentList; import com.sap.conn.idoc.IDocFactory; import com.sap.conn.idoc.IDocRepository; import com.sap.conn.idoc.jco.JCoIDoc; import com.sap.conn.jco.*; import com.sap.conn.jco.ext.Environment; import org.apache.axiom.om.OMElement; import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axis2.AxisFault; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.context.MessageContext; import org.apache.axis2.description.OutInAxisOperation; import org.apache.axis2.description.Parameter; import org.apache.axis2.description.TransportOutDescription; import org.apache.axis2.engine.AxisEngine; import org.apache.axis2.transport.OutTransportInfo; import org.apache.axis2.transport.TransportUtils; import org.apache.axis2.transport.base.AbstractTransportSender; import org.apache.axis2.util.MessageContextBuilder; import org.wso2.carbon.transports.sap.bapi.util.RFCConstants; import org.wso2.carbon.transports.sap.bapi.util.RFCMetaDataParser; import org.wso2.carbon.transports.sap.idoc.DefaultIDocXMLMapper; import org.wso2.carbon.transports.sap.idoc.IDocXMLMapper; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import java.io.ByteArrayInputStream; import java.net.URI; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * <code>SAPTransportSender </code> provides the ransport Sender implementation for SAP endpoints * */ public class SAPTransportSender extends AbstractTransportSender { private Map<String, IDocXMLMapper> xmlMappers = new HashMap<String, IDocXMLMapper>(); private IDocXMLMapper defaultMapper = new DefaultIDocXMLMapper(); /** * The SAP endpoint error code */ public static final String ERROR_CODE = "ERROR_CODE"; /** * Error while sending the Message through wire */ public static final int SAP_TRANSPORT_ERROR = 8000; /** * SAP destination error. Possibly something wrong with the remote R/* system */ public static final int SAP_DESTINATION_ERROR = 8001; @Override public void init(ConfigurationContext cfgCtx, TransportOutDescription trpOut) throws AxisFault { super.init(cfgCtx, trpOut); CarbonDestinationDataProvider provider = new CarbonDestinationDataProvider(); if (!Environment.isServerDataProviderRegistered()) { Environment.registerServerDataProvider(provider); } if (!Environment.isDestinationDataProviderRegistered()) { Environment.registerDestinationDataProvider(provider); } //check and initalize if XML mappers are declared Parameter xmlMappersParam = trpOut.getParameter(SAPConstants.CUSTOM_IDOC_XML_MAPPERS); if (xmlMappersParam != null) { OMElement mappersElt = xmlMappersParam.getParameterElement().getFirstElement(); Iterator mappers = mappersElt.getChildrenWithName(new QName(SAPConstants.XML_MAPPER_ELT)); try { while (mappers.hasNext()) { OMElement m = (OMElement) mappers.next(); String key = m.getAttributeValue(new QName(SAPConstants.XML_MAPPER_KEY_ATTR)); String value = m.getText().trim(); Class clazz = this.getClass().getClassLoader().loadClass(value); IDocXMLMapper mapper = (IDocXMLMapper) clazz.newInstance(); xmlMappers.put(key, mapper); } } catch (Exception e) { throw new AxisFault("Error while initializing the SAP transport sender", e); } } } /** * Send the SAP message to the SAP R/* system, Accepted URL format: idoc:/MyServer[?version=2] * @param messageContext axis2 message context * @param targetEPR SAP EPR * @param outTransportInfo out transport info * @throws AxisFault throws in case of an error */ public void sendMessage(MessageContext messageContext, String targetEPR, OutTransportInfo outTransportInfo) throws AxisFault { if (targetEPR == null) { throw new AxisFault("Cannot send an IDoc without a target SAP EPR"); } try { URI uri = new URI(targetEPR); String destName = uri.getPath().substring(1); JCoDestination destination = JCoDestinationManager.getDestination(destName); if (uri.getScheme().equals(SAPConstants.SAP_IDOC_PROTOCOL_NAME)) { IDocRepository iDocRepository = JCoIDoc.getIDocRepository(destination); String tid = destination.createTID(); IDocDocumentList iDocList = getIDocs(messageContext, iDocRepository); JCoIDoc.send(iDocList, getIDocVersion(uri), destination, tid); destination.confirmTID(tid); } else if (uri.getScheme().equals(SAPConstants.SAP_BAPI_PROTOCOL_NAME)) { try { OMElement payLoad,body; body = messageContext.getEnvelope().getBody(); payLoad = body.getFirstChildWithName(new QName(RFCConstants.BAPIRFC)); log.info("Received RFC/Meta DATA: " + payLoad); String rfcFunctionName = RFCMetaDataParser.getBAPIRFCFucntionName(payLoad); log.info("Looking up the BAPI/RFC function: " + rfcFunctionName + ". In the " + "meta data repository"); JCoFunction function = getRFCfunction(destination, rfcFunctionName); RFCMetaDataParser.processMetaDataDocument(payLoad, function); String responseXML = evaluateRFCfunction(function, destination); processResponse(messageContext, responseXML); } catch (Exception e) { sendFault(messageContext, e , SAP_TRANSPORT_ERROR); } } else { handleException("Invalid protocol name : " + uri.getScheme() + " in SAP URL"); } } catch (Exception e) { sendFault(messageContext,e, SAP_DESTINATION_ERROR); handleException("Error while sending an IDoc to the EPR : " + targetEPR, e); } } private char getIDocVersion(URI uri) { String query = uri.getQuery(); if (query != null && query.startsWith(SAPConstants.SAP_IDOC_VERSION)) { String version = query.substring(query.indexOf('=') + 1); if (SAPConstants.SAP_IDOC_VERSION_2.equals(version)) { return IDocFactory.IDOC_VERSION_2; } else if (SAPConstants.SAP_IDOC_VERSION_3.equals(version)) { return IDocFactory.IDOC_VERSION_3; } } return IDocFactory.IDOC_VERSION_DEFAULT; } /** * retrive IDOCs from message context * * @param msgContext Synapse Message Context * @param repo the repository to be used for querying the needed IDoc meta data information in * order to create the corresponding IDocDocumentList instance * @return A list of IDOcs * @throws Exception in case of an error */ private IDocDocumentList getIDocs(MessageContext msgContext, IDocRepository repo) throws Exception { Object mapper = msgContext.getOptions().getProperty(SAPConstants.CLIENT_XML_MAPPER_KEY); //check for any user defined xml mappers if (mapper != null && xmlMappers.containsKey(mapper.toString())) { return xmlMappers.get(mapper.toString()).getDocumentList(repo, msgContext); } else { return defaultMapper.getDocumentList(repo, msgContext); } } /** * Evaluate the BAPI/RFC function in a remote R/* system * @param function the BAPI/RFC function * @param destination jco destination * @return the result of the function execution * @throws AxisFault throws in case of an error */ private String evaluateRFCfunction(JCoFunction function, JCoDestination destination) throws AxisFault { log.info("Invoking the RFC function :" + function.getName()); try { function.execute(destination); } catch (JCoException e) { throw new AxisFault("Cloud not execute the RFC function: " + function, e); } JCoStructure returnStructure = null; try { returnStructure = function.getExportParameterList().getStructure("RETURN"); } catch (Exception ignore) { } // there seems to be some error that we need to report: TODO ? if (returnStructure != null && (!(returnStructure.getString("TYPE").equals("") || returnStructure.getString("TYPE").equals("S")))) { throw new AxisFault(returnStructure.getString("MESSAGE")); } return function.toXML(); } /** * Returns the BAPI/RFC function from the SAP repository * @param destination SAP JCO destination * @param rfcName the rfc name * @return the BAPI/RFC function * @throws AxisFault throws in case of an error */ private JCoFunction getRFCfunction(JCoDestination destination, String rfcName) throws AxisFault { log.info("Retriving the BAPI/RFC function : " + rfcName + " from the destination : " + destination); JCoFunction function = null; try { function = destination.getRepository().getFunction(rfcName); } catch (JCoException e) { throw new AxisFault("RFC function " + rfcName + " could not found in SAP system", e); } return function; } /** * Process and send the response of the RFC execution through axis engine * @param msgContext axis2 message context * @param payLoad RFC execution payload * @throws AxisFault throws in case of an error */ private void processResponse(MessageContext msgContext, String payLoad) throws AxisFault { if (!(msgContext.getAxisOperation() instanceof OutInAxisOperation)) { return; } try { MessageContext responseMessageContext = createResponseMessageContext(msgContext); ByteArrayInputStream bais = new ByteArrayInputStream(payLoad.getBytes()); SOAPEnvelope envelope = TransportUtils.createSOAPMessage(msgContext, bais, SAPConstants.SAP_CONTENT_TYPE); responseMessageContext.setEnvelope(envelope); AxisEngine.receive(responseMessageContext); log.info("Sending response out.."); } catch (XMLStreamException e) { throw new AxisFault("Error while processing response", e); } } /** * Send an axis fault if an error happened * @param msgContext axis2 message context * @param e the exception * @param errorCode error code of the error */ private void sendFault(MessageContext msgContext, Exception e , int errorCode) { //TODO Fix this properly try { MessageContext faultContext = MessageContextBuilder.createFaultMessageContext( msgContext, e); faultContext.setProperty(ERROR_CODE,errorCode); faultContext.setProperty("ERROR_MESSAGE",e.getMessage()); faultContext.setProperty("SENDING_FAULT", Boolean.TRUE); if (msgContext.getAxisOperation() != null && msgContext.getAxisOperation().getMessageReceiver() != null) { msgContext.getAxisOperation().getMessageReceiver().receive(faultContext); } else { log.error("Could not create the fault message.", e); } } catch (AxisFault axisFault) { log.fatal("Cloud not create the fault message.", axisFault); } } }