├── components └── sap │ ├── readme.txt │ ├── src │ └── main │ │ └── java │ │ └── org │ │ └── wso2 │ │ └── carbon │ │ └── transports │ │ └── sap │ │ ├── bapi │ │ ├── util │ │ │ ├── RFCConstants.java │ │ │ └── RFCMetaDataParser.java │ │ ├── SAPOutTransportInfo.java │ │ ├── BAPIEndpoint.java │ │ └── Axis2RFCHandler.java │ │ ├── idoc │ │ ├── Axis2IDocHandlerFactory.java │ │ ├── IDocXMLMapper.java │ │ ├── IDocEndpoint.java │ │ ├── util │ │ │ └── IDoCAdapterUtils.java │ │ ├── DefaultIDocXMLMapper.java │ │ └── Axis2IDocHandler.java │ │ ├── DefaultErrorListener.java │ │ ├── DefaultTIDHandler.java │ │ ├── SAPTransportListener.java │ │ ├── SAPConstants.java │ │ ├── CarbonDestinationDataProvider.java │ │ ├── SAPEndpoint.java │ │ └── SAPTransportSender.java │ └── pom.xml ├── .github ├── CODEOWNERS └── workflows │ ├── push_ubuntu_build.yml │ └── pull_request_ubuntu_build.yml ├── README.md ├── .gitignore ├── issue_template.md ├── pull_request_template.md ├── features └── org.wso2.carbon.sap.feature │ └── pom.xml ├── pom.xml └── LICENSE /components/sap/readme.txt: -------------------------------------------------------------------------------- 1 | Follow the steps to build the sap-transport.jar 2 | 3 | 1. Copy sapidoc3.jar and sapjco3.jar into the ./lib folder. 4 | 2. Then execute the maven build 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # See: https://help.github.com/articles/about-codeowners/ 5 | 6 | # These owners will be the default owners for everything in the repo. 7 | * @rosensilva 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sap-adapter 2 | SAP transport implementation 3 | 4 | ## Build Instructions 5 | 6 | 1. Copy **sapidoc3.jar** and **sapjco3.jar** to a directory. Lets call this 7 | 2. Build the repository using the following command 8 | ``` 9 | mvn -Dsap.lib.dir= clean install 10 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # Intelij 26 | *.iml 27 | .idea 28 | 29 | # target folders 30 | */target/** 31 | -------------------------------------------------------------------------------- /issue_template.md: -------------------------------------------------------------------------------- 1 | **Description:** 2 | 3 | 4 | **Suggested Labels:** 5 | 6 | 7 | **Suggested Assignees:** 8 | 9 | 10 | **Affected Product Version:** 11 | 12 | **OS, DB, other environment details and versions:** 13 | 14 | **Steps to reproduce:** 15 | 16 | 17 | **Related Issues:** 18 | -------------------------------------------------------------------------------- /.github/workflows/push_ubuntu_build.yml: -------------------------------------------------------------------------------- 1 | name: CI - Push request - Ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up JDK 11 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '11' 18 | distribution: 'temurin' 19 | cache: maven 20 | - name: Clone sap resources repo 21 | env: 22 | RESOURCE_REPO: ${{ secrets.WSO2_INTEGRATION_BOT_TOKEN }} 23 | uses: actions/checkout@v4 24 | with: 25 | repository: wso2-enterprise/wso2-sap-resources 26 | token: ${{ secrets.WSO2_INTEGRATION_BOT_TOKEN }} 27 | path: libs 28 | - name: Build with Maven 29 | run: mvn -Dsap.lib.dir=libs/wso2-sap-resources/resources/lib/. clean install 30 | - name: Upload test coverage to Codecov 31 | uses: codecov/codecov-action@v4.0.1 32 | with: 33 | flags: unit_tests 34 | token: ${{ secrets.CODECOV_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/workflows/pull_request_ubuntu_build.yml: -------------------------------------------------------------------------------- 1 | name: CI - Pull request - Ubuntu 2 | 3 | on: 4 | pull_request: 5 | branches: [ "master" ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up JDK 11 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '11' 18 | distribution: 'temurin' 19 | cache: maven 20 | - name: Clone sap resources repo 21 | env: 22 | RESOURCE_REPO: ${{ secrets.WSO2_INTEGRATION_BOT_TOKEN }} 23 | uses: actions/checkout@v4 24 | with: 25 | repository: wso2-enterprise/wso2-sap-resources 26 | token: ${{ secrets.WSO2_INTEGRATION_BOT_TOKEN }} 27 | path: libs 28 | - name: Build with Maven 29 | run: mvn -Dsap.lib.dir=libs/wso2-sap-resources/resources/lib/. clean install 30 | - name: Upload test coverage to Codecov 31 | uses: codecov/codecov-action@v4.0.1 32 | with: 33 | flags: unit_tests 34 | token: ${{ secrets.CODECOV_TOKEN }} 35 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/bapi/util/RFCConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WSO2, Inc. (http://wso2.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.wso2.carbon.transports.sap.bapi.util; 17 | 18 | import javax.xml.namespace.QName; 19 | 20 | /** 21 | * BAPI/RFC constants 22 | */ 23 | public class RFCConstants { 24 | /** 25 | * BAPI/RFC function name 26 | */ 27 | public static final String BAPIRFC = "bapirfc"; 28 | /** 29 | * name in the rfc configuration 30 | */ 31 | public static final QName NAME_Q = new QName("name"); 32 | /** 33 | * id in rfc configuation 34 | */ 35 | public static final QName ID_Q = new QName("id"); 36 | /** 37 | * structure in payload 38 | */ 39 | public static final String STRUCTURE = "structure"; 40 | /** 41 | * table in payload 42 | */ 43 | public static final String TABLE = "table"; 44 | /** 45 | * field in payload 46 | */ 47 | public static final String FIELD = "field"; 48 | } 49 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/idoc/Axis2IDocHandlerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap.idoc; 21 | 22 | import com.sap.conn.idoc.jco.JCoIDocHandler; 23 | import com.sap.conn.idoc.jco.JCoIDocHandlerFactory; 24 | import com.sap.conn.idoc.jco.JCoIDocServerContext; 25 | import org.apache.axis2.transport.base.threads.WorkerPool; 26 | 27 | /** 28 | * Axis2IDocHandlerFactory provides a factory for {@link JCoIDocHandlerFactory } 29 | */ 30 | public class Axis2IDocHandlerFactory implements JCoIDocHandlerFactory { 31 | 32 | private Axis2IDocHandler handler; 33 | 34 | public Axis2IDocHandlerFactory(IDocEndpoint endpoint, WorkerPool workerPool) { 35 | handler = new Axis2IDocHandler(workerPool, endpoint); 36 | } 37 | 38 | public JCoIDocHandler getIDocHandler(JCoIDocServerContext jCoIDocServerContext) { 39 | return handler; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/idoc/IDocXMLMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap.idoc; 21 | 22 | import com.sap.conn.idoc.IDocDocumentList; 23 | import com.sap.conn.idoc.IDocRepository; 24 | 25 | import org.apache.axis2.context.MessageContext; 26 | import org.apache.axis2.AxisFault; 27 | 28 | 29 | /** 30 | * IDocXMLMapper provides a way to map an IDoc to XML in a user preferred way 31 | */ 32 | public interface IDocXMLMapper { 33 | 34 | /** 35 | * Returns the IDoc document list 36 | * @param repo the IDoc repository 37 | * @param msgContext axis2 message context 38 | * @return the mapped IDoc list 39 | * @throws AxisFault throws in case of an error 40 | */ 41 | public IDocDocumentList getDocumentList(IDocRepository repo, 42 | MessageContext msgContext) throws AxisFault; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/bapi/SAPOutTransportInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | package org.wso2.carbon.transports.sap.bapi; 20 | 21 | import org.apache.axiom.om.OMElement; 22 | import org.apache.axis2.transport.OutTransportInfo; 23 | 24 | /** 25 | * This class is used to handle the synchronous BAPI listener's response payload. 26 | */ 27 | public class SAPOutTransportInfo implements OutTransportInfo { 28 | 29 | private OMElement payload; 30 | private String protocol; 31 | 32 | public void setPayload(OMElement payload) { 33 | 34 | this.payload = payload; 35 | } 36 | 37 | public OMElement getPayload() { 38 | 39 | return this.payload; 40 | } 41 | 42 | public void setProtocol(String protocol) { 43 | 44 | this.protocol = protocol; 45 | } 46 | 47 | public String getProtocol() { 48 | 49 | return this.protocol; 50 | } 51 | 52 | @Override 53 | public void setContentType(String s) { 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/DefaultErrorListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap; 21 | 22 | import com.sap.conn.jco.server.JCoServerErrorListener; 23 | import com.sap.conn.jco.server.JCoServerExceptionListener; 24 | import com.sap.conn.jco.server.JCoServer; 25 | import com.sap.conn.jco.server.JCoServerContextInfo; 26 | import org.apache.commons.logging.Log; 27 | import org.apache.commons.logging.LogFactory; 28 | 29 | /** 30 | * DefaultErrorListener provides a default implementation of the error listner 31 | */ 32 | public class DefaultErrorListener implements JCoServerErrorListener, JCoServerExceptionListener { 33 | 34 | private static final Log log = LogFactory.getLog(DefaultErrorListener.class); 35 | 36 | public void serverErrorOccurred(JCoServer server, String connectionId, 37 | JCoServerContextInfo ctx, Error error) { 38 | log.error("Error occured on : " + server.getProgramID() + " and connection : " + 39 | connectionId, error); 40 | } 41 | 42 | public void serverExceptionOccurred(JCoServer server, String connectionId, 43 | JCoServerContextInfo ctx, Exception error) { 44 | log.error("Exception occured on : " + server.getProgramID() + " and connection : " + 45 | connectionId, error); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/DefaultTIDHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap; 21 | 22 | import com.sap.conn.jco.server.JCoServerTIDHandler; 23 | import com.sap.conn.jco.server.JCoServerContext; 24 | import org.apache.commons.logging.Log; 25 | import org.apache.commons.logging.LogFactory; 26 | 27 | /** 28 | * DefaultTIDHandler provides a defaul implementation of the SAP tx handling 29 | * mechanishm 30 | */ 31 | public class DefaultTIDHandler implements JCoServerTIDHandler { 32 | 33 | private static final Log log = LogFactory.getLog(DefaultTIDHandler.class); 34 | 35 | public boolean checkTID(JCoServerContext jCoServerContext, String s) { 36 | log.info("Checking TID: " + s + ". Destination : " + 37 | jCoServerContext.getConnectionAttributes().getDestination()); 38 | return true; 39 | } 40 | 41 | public void commit(JCoServerContext jCoServerContext, String s) { 42 | log.info("Committing TID: " + s + ". Destination : " + 43 | jCoServerContext.getConnectionAttributes().getDestination()); 44 | } 45 | 46 | public void confirmTID(JCoServerContext jCoServerContext, String s) { 47 | log.info("Confirming TID: " + s + ". Destination : " + 48 | jCoServerContext.getConnectionAttributes().getDestination()); 49 | } 50 | 51 | public void rollback(JCoServerContext jCoServerContext, String s) { 52 | log.info("Rolling back TID: " + s + ". Destination : " + 53 | jCoServerContext.getConnectionAttributes().getDestination()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | > Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc. 3 | 4 | ## Goals 5 | > Describe the solutions that this feature/fix will introduce to resolve the problems described above 6 | 7 | ## Approach 8 | > Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI (email documentation@wso2.com to review all UI text). Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here. 9 | 10 | ## User stories 11 | > Summary of user stories addressed by this change> 12 | 13 | ## Release note 14 | > Brief description of the new feature or bug fix as it will appear in the release notes 15 | 16 | ## Documentation 17 | > Link(s) to product documentation that addresses the changes of this PR. If no doc impact, enter “N/A” plus brief explanation of why there’s no doc impact 18 | 19 | ## Training 20 | > Link to the PR for changes to the training content in https://github.com/wso2/WSO2-Training, if applicable 21 | 22 | ## Certification 23 | > Type “Sent” when you have provided new/updated certification questions, plus four answers for each question (correct answer highlighted in bold), based on this change. Certification questions/answers should be sent to certification@wso2.com and NOT pasted in this PR. If there is no impact on certification exams, type “N/A” and explain why. 24 | 25 | ## Marketing 26 | > Link to drafts of marketing content that will describe and promote this feature, including product page changes, technical articles, blog posts, videos, etc., if applicable 27 | 28 | ## Automation tests 29 | - Unit tests 30 | > Code coverage information 31 | - Integration tests 32 | > Details about the test cases and coverage 33 | 34 | ## Security checks 35 | - Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes/no 36 | - Ran FindSecurityBugs plugin and verified report? yes/no 37 | - Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes/no 38 | 39 | ## Samples 40 | > Provide high-level details about the samples related to this feature 41 | 42 | ## Related PRs 43 | > List any other related PRs 44 | 45 | ## Migrations (if applicable) 46 | > Describe migration steps and platforms on which migration has been tested 47 | 48 | ## Test environment 49 | > List all JDK versions, operating systems, databases, and browser/versions on which this feature/fix was tested 50 | 51 | ## Learning 52 | > Describe the research phase and any blog posts, patterns, libraries, or add-ons you used to solve the problem. -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/SAPTransportListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap; 21 | 22 | import com.sap.conn.jco.ext.Environment; 23 | import org.apache.axis2.AxisFault; 24 | import org.apache.axis2.transport.base.AbstractTransportListenerEx; 25 | import org.wso2.carbon.transports.sap.bapi.BAPIEndpoint; 26 | import org.wso2.carbon.transports.sap.idoc.IDocEndpoint; 27 | 28 | /** 29 | * SAPTransportListener provides the transport listener implementation for SAP adapter 30 | * Listner parameters can be configured either in axis2.xml ( golbal) or using service level parameters 31 | * (services.xml or parameters in proxy service configurations) 32 | */ 33 | public class SAPTransportListener extends AbstractTransportListenerEx { 34 | 35 | @Override 36 | public void doInit() throws AxisFault { 37 | CarbonDestinationDataProvider provider = new CarbonDestinationDataProvider(); 38 | if (!Environment.isServerDataProviderRegistered()) { 39 | Environment.registerServerDataProvider(provider); 40 | } 41 | if (!Environment.isDestinationDataProviderRegistered()) { 42 | Environment.registerDestinationDataProvider(provider); 43 | } 44 | //super.init(cfgCtx, transportIn); 45 | } 46 | 47 | protected SAPEndpoint createEndpoint() { 48 | String transportName = getTransportName(); 49 | if (SAPConstants.SAP_IDOC_PROTOCOL_NAME.equals(transportName)) { 50 | return new IDocEndpoint(); 51 | } else if (SAPConstants.SAP_BAPI_PROTOCOL_NAME.equals(transportName)) { 52 | return new BAPIEndpoint(); 53 | } else { 54 | throw new UnsupportedOperationException("Protocol name: " + transportName + " is not " + 55 | "supported"); 56 | } 57 | } 58 | 59 | protected void startEndpoint(SAPEndpoint endpoint) throws AxisFault { 60 | endpoint.startEndpoint(workerPool); 61 | } 62 | 63 | protected void stopEndpoint(SAPEndpoint endpoint) { 64 | endpoint.stopEndpoint(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/idoc/IDocEndpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap.idoc; 21 | 22 | import org.apache.axis2.transport.base.threads.WorkerPool; 23 | import org.apache.axis2.AxisFault; 24 | import org.wso2.carbon.transports.sap.SAPEndpoint; 25 | import com.sap.conn.idoc.jco.JCoIDocServer; 26 | import com.sap.conn.idoc.jco.JCoIDoc; 27 | /** 28 | * IDocEndpoint class 29 | *

30 | * This class represents a AXIS2 endpoint for IDOC data exchange.Handles all incoming IDOC transfer 31 | * calls are handled through registered handlers 32 | *

33 | * @see com.sap.conn.idoc.jco.JCoIDocHandlerFactory 34 | */ 35 | public class IDocEndpoint extends SAPEndpoint { 36 | 37 | private JCoIDocServer server; 38 | 39 | /** 40 | * Start the IDoc endpoint 41 | * @param workerPool the worker thread pool 42 | * @throws AxisFault throws in case of an error 43 | */ 44 | public void startEndpoint(WorkerPool workerPool) throws AxisFault { 45 | if (log.isDebugEnabled()) { 46 | log.debug("Starting the IDoc endpoint : " + serverName); 47 | } 48 | try { 49 | server = JCoIDoc.getServer(serverName); 50 | server.setIDocHandlerFactory(new Axis2IDocHandlerFactory(this, workerPool)); 51 | setupOptionalFeatures(server); 52 | server.start(); 53 | log.info("IDoc server started with server name : " + serverName + " and " + 54 | "program ID : " + server.getProgramID()); 55 | } catch (Exception e) { 56 | handleException("Error while initializing the SAP IDoc server", e); 57 | } 58 | } 59 | 60 | public void stopEndpoint() { 61 | if (log.isDebugEnabled()) { 62 | log.debug("Stopping the IDoc endpoint : " + serverName); 63 | } 64 | 65 | server.stop(); 66 | server.release(); 67 | 68 | if (!waitForServerStop(server)) { 69 | log.warn("IDoc server : " + serverName + " is taking an unusually long time to stop."); 70 | } else { 71 | log.info("IDoc server : " + serverName + " stopped"); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/bapi/BAPIEndpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap.bapi; 21 | 22 | import org.apache.axis2.transport.base.threads.WorkerPool; 23 | import org.apache.axis2.AxisFault; 24 | import org.wso2.carbon.transports.sap.SAPEndpoint; 25 | import com.sap.conn.jco.server.*; 26 | 27 | /** 28 | * BAPIEndpoint class 29 | *

30 | * This class represents a AXIS2 endpoint for Bapi Function calls.Handles all incoming bapi/rfc calls 31 | * through registered handlers 32 | *

33 | * @see com.sap.conn.jco.server.JCoServerFunctionHandler 34 | */ 35 | public class BAPIEndpoint extends SAPEndpoint { 36 | 37 | private JCoServer server; 38 | 39 | public void startEndpoint(WorkerPool workerPool) throws AxisFault { 40 | if (log.isDebugEnabled()) { 41 | log.debug("Starting the JCo endpoint : " + serverName); 42 | } 43 | 44 | try { 45 | server = JCoServerFactory.getServer(serverName); 46 | DefaultServerHandlerFactory.FunctionHandlerFactory factory = 47 | new DefaultServerHandlerFactory.FunctionHandlerFactory(); 48 | // register message handlers for bapirfc calls 49 | factory.registerGenericHandler(new Axis2RFCHandler(this, workerPool)); 50 | // register the server hanlder factory 51 | server.setCallHandlerFactory(factory); 52 | setupOptionalFeatures(server); 53 | server.start(); 54 | log.info("JCo server started with server name : " + serverName + " and " + 55 | "program ID : " + server.getProgramID()); 56 | } catch (Exception e) { 57 | handleException("Error while initializing the SAP JCo server", e); 58 | } 59 | } 60 | 61 | public void stopEndpoint() { 62 | if (log.isDebugEnabled()) { 63 | log.debug("Stopping the JCo endpoint : " + serverName); 64 | } 65 | server.stop(); 66 | server.release(); 67 | 68 | if (!waitForServerStop(server)) { 69 | log.warn("JCo server : " + serverName + " is taking an unusually long time to stop."); 70 | } else { 71 | log.info("JCo server : " + serverName + " stopped"); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /features/org.wso2.carbon.sap.feature/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | org.wso2.carbon.mediation 22 | sap-adapter 23 | 4.7.37-SNAPSHOT 24 | ../../pom.xml 25 | 26 | 27 | 4.0.0 28 | org.wso2.carbon.transports.sap.feature 29 | pom 30 | WSO2 Carbon - SAP Adapter Feature 31 | 32 | http://wso2.org 33 | This feature contains the bundles required for sap 34 | 35 | 36 | 37 | org.wso2.carbon.mediation 38 | org.wso2.carbon.transports.sap 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.wso2.maven 46 | carbon-p2-plugin 47 | ${carbon.p2.plugin.version} 48 | 49 | 50 | 4-p2-feature-generation 51 | package 52 | 53 | p2-feature-gen 54 | 55 | 56 | 57 | 58 | 59 | org.wso2.carbon.mediation:org.wso2.carbon.transports.sap:${project.version} 60 | 61 | 62 | org.wso2.carbon.transports.sap 63 | ../../etc/feature.properties 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/idoc/util/IDoCAdapterUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WSO2, Inc. (http://wso2.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.wso2.carbon.transports.sap.idoc.util; 17 | 18 | import org.apache.axiom.om.OMElement; 19 | import org.apache.axiom.om.OMFactory; 20 | import org.apache.axiom.om.OMAbstractFactory; 21 | import org.apache.commons.logging.Log; 22 | import org.apache.commons.logging.LogFactory; 23 | 24 | import javax.xml.namespace.QName; 25 | import java.util.UUID; 26 | import java.util.Iterator; 27 | 28 | /** 29 | * IDoCAdapterUtils provides some utility method for SAP adapter code 30 | */ 31 | public class IDoCAdapterUtils { 32 | 33 | private static Log log = LogFactory.getLog(IDoCAdapterUtils.class); 34 | 35 | private static final QName IDOC_Q = new QName("IDOC"); 36 | 37 | private static final QName ARCKEY_Q = new QName("ARCKEY"); 38 | 39 | private static final QName EDI_DC40_Q = new QName("EDI_DC40"); 40 | 41 | protected static final OMFactory fac = OMAbstractFactory.getOMFactory(); 42 | 43 | /** 44 | * This method will stamp a ARCKEY into the control segment of the IDoC 45 | * see http://help.sap.com/saphelp_nw04/helpdata/en/13/95244269625633e10000000a155106/content.htm 46 | * for more information 47 | * @param element The idoc element 48 | * @param msgId the message ID of the message 49 | */ 50 | public static void stampArcKey(OMElement element, String msgId) { 51 | String arcKey; 52 | boolean isStampArcKey = false; 53 | 54 | // msgId can be null if we disable addressing or use REST 55 | // in that case generate a UUID for the ARCKEY 56 | if (msgId == null) { 57 | arcKey = generateUUID(); 58 | } else { 59 | arcKey = msgId; 60 | } 61 | Iterator itr = element.getChildrenWithName(IDOC_Q); 62 | while (itr.hasNext()){ 63 | OMElement idocElement = (OMElement) itr.next(); 64 | OMElement controlElement = idocElement.getFirstChildWithName(EDI_DC40_Q); 65 | OMElement arcKeyElement = controlElement.getFirstChildWithName(ARCKEY_Q); 66 | 67 | // there is no ARCKEY, so stamp a one 68 | if (arcKeyElement == null || arcKeyElement.getText().equals("")) { 69 | isStampArcKey = true; 70 | OMElement newArcKeyEle = fac.createOMElement(ARCKEY_Q); 71 | newArcKeyEle.setText(arcKey); 72 | controlElement.addChild(newArcKeyEle); 73 | } 74 | } 75 | if(log.isDebugEnabled() && isStampArcKey){ 76 | log.debug("The ARCKEY ' " + arcKey + " ', stamped into the control segment"); 77 | } 78 | } 79 | 80 | public static String getProperty(String name){ 81 | return System.getProperty(name); 82 | } 83 | 84 | private static String generateUUID() { 85 | return UUID.randomUUID().toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/idoc/DefaultIDocXMLMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap.idoc; 21 | 22 | import com.sap.conn.idoc.IDocDocumentList; 23 | import com.sap.conn.idoc.IDocRepository; 24 | import com.sap.conn.idoc.IDocXMLProcessor; 25 | import com.sap.conn.idoc.jco.JCoIDoc; 26 | import org.apache.axiom.om.OMElement; 27 | import org.apache.axiom.soap.SOAPBody; 28 | import org.apache.axis2.AxisFault; 29 | import org.apache.axis2.context.MessageContext; 30 | import org.apache.commons.logging.Log; 31 | import org.apache.commons.logging.LogFactory; 32 | import org.wso2.carbon.transports.sap.SAPConstants; 33 | import org.wso2.carbon.transports.sap.idoc.util.IDoCAdapterUtils; 34 | 35 | import java.io.ByteArrayInputStream; 36 | import java.io.ByteArrayOutputStream; 37 | import java.io.Closeable; 38 | import java.io.IOException; 39 | 40 | /** 41 | *

42 | * DefaultIDocXMLMapper basically implements messagecontext to IDOCs parsing. 43 | *

44 | */ 45 | public class DefaultIDocXMLMapper implements IDocXMLMapper { 46 | 47 | private Log log = LogFactory.getLog(getClass()); 48 | 49 | private IDocXMLProcessor xmlProcessor = JCoIDoc.getIDocFactory().getIDocXMLProcessor(); 50 | 51 | public IDocDocumentList getDocumentList(IDocRepository repo, 52 | MessageContext msgContext) throws AxisFault { 53 | 54 | ByteArrayInputStream bais = null; 55 | ByteArrayOutputStream baos = null; 56 | 57 | try { 58 | baos = new ByteArrayOutputStream(); 59 | SOAPBody body = msgContext.getEnvelope().getBody(); 60 | OMElement idocElement = body.getFirstElement(); 61 | String stampArcKey = IDoCAdapterUtils.getProperty("stamp_s_arc_key"); 62 | if (stampArcKey == null || !stampArcKey.equals("false")) { 63 | IDoCAdapterUtils.stampArcKey(idocElement, msgContext.getMessageID()); 64 | } 65 | idocElement.serialize(baos); 66 | baos.flush(); 67 | 68 | bais = new ByteArrayInputStream(baos.toByteArray()); 69 | Object prop = msgContext.getOptions().getProperty( 70 | SAPConstants.CLIENT_XML_PARSER_OPTIONS); 71 | if (prop != null) { 72 | return xmlProcessor.parse(repo, bais, Integer.parseInt(prop.toString())); 73 | } else { 74 | return xmlProcessor.parse(repo, bais); 75 | } 76 | } catch (Exception e) { 77 | throw new AxisFault("Error while retrieving IDocs from the message context", e); 78 | } finally { 79 | closeStream(bais); 80 | closeStream(baos); 81 | } 82 | } 83 | 84 | private void closeStream(Closeable stream) { 85 | try { 86 | stream.close(); 87 | } catch (IOException e) { 88 | log.error("Error while closing the stream", e); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/SAPConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap; 21 | 22 | /** 23 | * SAP adapter constants 24 | */ 25 | public class SAPConstants { 26 | 27 | public static final String SAP_IDOC_PROTOCOL_NAME = "idoc"; 28 | public static final String SAP_IDOC_PROTOCOL_PREFIX = SAP_IDOC_PROTOCOL_NAME + ":/"; 29 | 30 | public static final String SAP_BAPI_PROTOCOL_NAME = "bapi"; 31 | public static final String SAP_BAPI_PROTOCOL_PREFIX = SAP_BAPI_PROTOCOL_NAME + ":/"; 32 | 33 | public static final String SAP_ENABLED = "enabled"; 34 | 35 | /* Transport listener parameters */ 36 | public static final String SERVER_NAME_PARAM = "transport.sap.serverName"; 37 | public static final String ENABLE_ERROR_LISTENER_PARAM = "transport.sap.enableErrorListener"; 38 | public static final String ENABLE_TID_HANDLER_PARAM = "transport.sap.enableTIDHandler"; 39 | public static final String ENABLE_SYNC_BAPI_LISTENER = "transport.sap.enableSyncBapiListener"; 40 | public static final String RESPONSE_TIMEOUT = "transport.sap.responseTimeout"; 41 | public static final String CONNECTIONS_PARAM = "transport.sap.connections"; 42 | public static final String CUSTOM_ERROR_LISTENER_PARAM = "transport.sap.customErrorListener"; 43 | public static final String CUSTOM_EXCEPTION_LISTENER_PARAM = "transport.sap.customExceptionListener"; 44 | public static final String CUSTOM_TID_HANDLER_PARAM = "transport.sap.customTIDHandler"; 45 | public static final String TRANSACTION_COMMIT_PARAM = "transport.sap.transactionCommit"; 46 | public static final String TRANSACTION_SAP_LOGON = "transport.sap.logon"; 47 | public static final String TRANSPORT_SAP_EXTCOMPANY = "transport.sap.EXTCOMPANY"; 48 | public static final String TRANSPORT_SAP_EXTPRODUCT = "transport.sap.EXTPRODUCT"; 49 | public static final String TRANSPORT_SAP_INTERFACE = "transport.sap.INTERFACE"; 50 | public static final String TRANSPORT_SAP_VERSION = "transport.sap.VERSION"; 51 | public static final String EXTCOMPANY = "EXTCOMPANY"; 52 | public static final String EXTPRODUCT = "EXTPRODUCT"; 53 | public static final String INTERFACE = "INTERFACE"; 54 | public static final String VERSION = "VERSION"; 55 | public static final String BABI_XMI_LOGON = "BAPI_XMI_LOGON"; 56 | 57 | /* Transport sender parameters */ 58 | public static final String CUSTOM_IDOC_XML_MAPPERS = "transport.sap.customXMLMappers"; 59 | public static final String SAP_IDOC_VERSION = "version"; 60 | 61 | public static final int SAP_SERVER_DEFAULT_CONNECTIONS = 1; 62 | public static final String SAP_IDOC_VERSION_2 = "2"; 63 | public static final String SAP_IDOC_VERSION_3 = "3"; 64 | 65 | public static final String SAP_CONTENT_TYPE = "application/xml"; 66 | 67 | public static final String XML_MAPPER_ELT = "mapper"; 68 | public static final String XML_MAPPER_KEY_ATTR = "key"; 69 | 70 | /* Client options */ 71 | public static final String CLIENT_XML_MAPPER_KEY = "transport.sap.xmlMapper"; 72 | public static final String CLIENT_XML_PARSER_OPTIONS = "transport.sap.xmlParserOptions"; 73 | 74 | /* bapi for transaction */ 75 | public static final String BAPI_TRANSACTION_COMMIT = "BAPI_TRANSACTION_COMMIT"; 76 | public static final String BAPI_TRANSACTION_ROLLBACK = "BAPI_TRANSACTION_ROLLBACK"; 77 | 78 | public static final String SECRET_ALIAS_PREFIX = "secretAlias:"; 79 | } 80 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/CarbonDestinationDataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap; 21 | 22 | import com.sap.conn.jco.ext.DestinationDataProvider; 23 | import com.sap.conn.jco.ext.DestinationDataEventListener; 24 | import com.sap.conn.jco.ext.ServerDataProvider; 25 | import com.sap.conn.jco.ext.ServerDataEventListener; 26 | 27 | import java.nio.file.Paths; 28 | import java.util.Properties; 29 | import java.io.File; 30 | import java.io.FileInputStream; 31 | import java.io.IOException; 32 | 33 | import org.apache.commons.logging.Log; 34 | import org.apache.commons.logging.LogFactory; 35 | import org.wso2.securevault.SecretResolver; 36 | import org.wso2.securevault.SecretResolverFactory; 37 | import org.wso2.securevault.commons.MiscellaneousUtil; 38 | 39 | /** 40 | * SAP requires to provide destination/server properties. CarbonDestinationDataProvider 41 | * provides a carbon specifc implementation where the server properties are loaded from a property 42 | * file *.serv * and client properties are loaded from a property file called *.dest 43 | */ 44 | public class CarbonDestinationDataProvider implements DestinationDataProvider, ServerDataProvider { 45 | 46 | private static final Log log = LogFactory.getLog(CarbonDestinationDataProvider.class); 47 | 48 | /** 49 | * Returns the client/server SAP properties 50 | * @param server server or client properties 51 | * @return SAP properties 52 | */ 53 | public Properties getServerProperties(String server) { 54 | File file = getConfigurationFile(server, true); 55 | if (file != null) { 56 | if (log.isDebugEnabled()) { 57 | log.debug("Loading server configuration from: " + file.getPath()); 58 | } 59 | 60 | try { 61 | Properties props = new Properties(); 62 | props.load(new FileInputStream(file)); 63 | return props; 64 | } catch (IOException e) { 65 | log.error("Error while loading server configuration from: " + file.getPath(), e); 66 | } 67 | } 68 | 69 | return null; 70 | } 71 | 72 | public void setServerDataEventListener(ServerDataEventListener listener) { 73 | 74 | } 75 | 76 | public Properties getDestinationProperties(String destination) { 77 | 78 | Properties props = null; 79 | File file = getConfigurationFile(destination, false); 80 | if (file != null) { 81 | if (log.isDebugEnabled()) { 82 | log.debug("Loading destination configuration from: " + file.getPath()); 83 | } 84 | 85 | try { 86 | props = new Properties(); 87 | props.load(new FileInputStream(file)); 88 | } catch (IOException e) { 89 | log.error("Error while loading destination configuration from: " + file.getPath(), e); 90 | } 91 | SecretResolver secretResolver = SecretResolverFactory.create(props); 92 | for (String key : props.stringPropertyNames()) { 93 | if (secretResolver != null && secretResolver.isInitialized()) { 94 | String value = props.getProperty(key); 95 | if (value != null) { 96 | if (value.startsWith(SAPConstants.SECRET_ALIAS_PREFIX)) { 97 | value = value.split(SAPConstants.SECRET_ALIAS_PREFIX)[1]; 98 | value = secretResolver.isTokenProtected(value) ? secretResolver.resolve(value) : value; 99 | } else { 100 | value = MiscellaneousUtil.resolve(value, secretResolver); 101 | } 102 | } 103 | props.put(key, value); 104 | } 105 | } 106 | return props; 107 | } 108 | 109 | return null; 110 | } 111 | 112 | public void setDestinationDataEventListener(DestinationDataEventListener listener) { 113 | 114 | } 115 | 116 | public boolean supportsEvents() { 117 | return false; 118 | } 119 | 120 | private File getConfigurationFile(String destination, boolean server) { 121 | String fileName = destination + "." + (server ? "server" : "dest"); 122 | String confPath = System.getProperty("conf.location"); 123 | if (confPath == null) { 124 | confPath = Paths.get("conf").toString(); 125 | } 126 | File file1 = Paths.get(confPath, "sap", fileName).toFile(); 127 | if (file1.exists()) { 128 | return file1; 129 | } 130 | 131 | File file2 = new File(fileName); 132 | if (file2.exists()) { 133 | return file2; 134 | } 135 | 136 | log.warn("JCo configuration file for the destination : " + destination + " does not " + 137 | "exist - Please specify the JCo configuration in " + file1.getPath() + " or " + 138 | file2.getPath()); 139 | return null; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /components/sap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | org.wso2.carbon.mediation 22 | sap-adapter 23 | 4.7.37-SNAPSHOT 24 | ../../pom.xml 25 | 26 | 27 | 4.0.0 28 | org.wso2.carbon.transports.sap 29 | ${packaging.type} 30 | WSO2 Carbon - SAP transport 31 | WSO2 Carbon - SAP Adapter 32 | 33 | 34 | 35 | 36 | maven-antrun-plugin 37 | 38 | 39 | 40 | run 41 | 42 | compile 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ant-contrib 70 | ant-contrib 71 | 20020829 72 | 73 | 74 | 75 | 76 | 77 | org.apache.felix 78 | maven-bundle-plugin 79 | true 80 | 81 | 82 | ${project.artifactId} 83 | ${project.artifactId} 84 | * 85 | 86 | org.wso2.carbon.transports.sap.*, 87 | 88 | 89 | org.apache.axis2.*; version="${axis2.osgi.version.range}", 90 | org.apache.axiom.*; version="${axiom.osgi.version.range}", 91 | *;resolution:=optional 92 | 93 | 94 | 95 | 96 | 97 | maven-compiler-plugin 98 | 99 | 100 | **/*.* 101 | 102 | 103 | 104 | 105 | comp 106 | compile 107 | 108 | 109 | test-comp 110 | test-compile 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | org.apache.axis2.wso2 120 | axis2 121 | 122 | 123 | org.ops4j.pax.logging 124 | pax-logging-api 125 | 126 | 127 | org.apache.axis2 128 | axis2-transport-base 129 | 130 | 131 | org.apache.ws.commons.axiom 132 | axiom-api 133 | 134 | 135 | xml-apis 136 | xml-apis 137 | 138 | 139 | xerces 140 | xercesImpl 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/idoc/Axis2IDocHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap.idoc; 21 | 22 | import com.sap.conn.idoc.jco.JCoIDocHandler; 23 | import com.sap.conn.idoc.jco.JCoIDoc; 24 | import com.sap.conn.idoc.IDocDocumentList; 25 | import com.sap.conn.idoc.IDocXMLProcessor; 26 | import com.sap.conn.jco.server.JCoServerContext; 27 | import org.apache.axis2.transport.base.threads.WorkerPool; 28 | import org.apache.axis2.transport.TransportUtils; 29 | import org.apache.axis2.context.MessageContext; 30 | import org.apache.axis2.engine.AxisEngine; 31 | import org.apache.axiom.soap.SOAPEnvelope; 32 | import org.apache.commons.io.IOUtils; 33 | import org.apache.commons.logging.Log; 34 | import org.apache.commons.logging.LogFactory; 35 | import org.wso2.carbon.transports.sap.idoc.IDocEndpoint; 36 | import org.wso2.carbon.transports.sap.SAPConstants; 37 | import org.wso2.carbon.transports.sap.idoc.util.IDoCAdapterUtils; 38 | 39 | import java.io.*; 40 | 41 | /** 42 | * This class handles IDOCs returned from the SAP gateway. 43 | *

44 | * This class encapsulates workers for each idoc request recieved. Workers are responsible for 45 | * handling IDOCs and converting them to SOAP format 46 | *

47 | */ 48 | public class Axis2IDocHandler implements JCoIDocHandler { 49 | 50 | private static final Log log = LogFactory.getLog(Axis2IDocHandler.class); 51 | 52 | private WorkerPool workerPool; 53 | private IDocEndpoint endpoint; 54 | private IDocXMLProcessor xmlProcessor; 55 | 56 | public Axis2IDocHandler(WorkerPool workerPool, IDocEndpoint endpoint) { 57 | this.workerPool = workerPool; 58 | this.endpoint = endpoint; 59 | this.xmlProcessor = JCoIDoc.getIDocFactory().getIDocXMLProcessor(); 60 | } 61 | 62 | /** 63 | * Handling the incoming requests 64 | * @param jCoServerContext 65 | * server environemnt context 66 | * @param iDocDocumentList 67 | * IDOCs returned by the SAP gateway 68 | */ 69 | public void handleRequest(JCoServerContext jCoServerContext, 70 | IDocDocumentList iDocDocumentList) { 71 | if (log.isDebugEnabled()) { 72 | log.debug("New IDoc received"); 73 | } 74 | workerPool.execute(new IDocWorker(jCoServerContext, iDocDocumentList)); 75 | } 76 | 77 | private class IDocWorker implements Runnable { 78 | 79 | private JCoServerContext serverContext; 80 | private IDocDocumentList docList; 81 | 82 | private IDocWorker(JCoServerContext serverContext, IDocDocumentList docList) { 83 | this.serverContext = serverContext; 84 | this.docList = docList; 85 | } 86 | 87 | public void run() { 88 | if (log.isDebugEnabled()) { 89 | log.debug("Starting a new IDoc worker thread to process the incoming request"); 90 | } 91 | 92 | ByteArrayInputStream bais = null; 93 | ByteArrayOutputStream baos = null; 94 | OutputStreamWriter osw = null; 95 | 96 | try { 97 | baos = new ByteArrayOutputStream(); 98 | osw = new OutputStreamWriter(baos, "UTF8"); 99 | //convert recieved idocs to an output format 100 | xmlProcessor.render(docList, osw); 101 | osw.flush(); 102 | String output = baos.toString(); 103 | if (output.contains("")) { 104 | output = output.replaceFirst("1.1", "1.0"); 105 | } 106 | 107 | MessageContext msgContext = endpoint.createMessageContext(); 108 | msgContext.setIncomingTransportName(SAPConstants.SAP_IDOC_PROTOCOL_NAME); 109 | 110 | if (log.isDebugEnabled()) { 111 | log.debug("Creating SOAP envelope from the IDoc"); 112 | } 113 | 114 | bais = new ByteArrayInputStream(output.getBytes()); 115 | if (log.isDebugEnabled()) { 116 | //just print the recieved idocs to System 117 | StringBuffer buffer = new StringBuffer("Received IDoc content: "); 118 | ByteArrayInputStream loggingStream = new ByteArrayInputStream(baos.toByteArray()); 119 | int len; 120 | byte[] data = new byte[1024]; 121 | while ((len = loggingStream.read(data)) != -1) { 122 | buffer.append(new String(data, 0, len)); 123 | } 124 | log.debug(buffer.toString()); 125 | } 126 | //build SOAP enevelope encapsulating the IDOCs in xml format 127 | SOAPEnvelope envelope = TransportUtils.createSOAPMessage(msgContext, bais, 128 | SAPConstants.SAP_CONTENT_TYPE); 129 | msgContext.setEnvelope(envelope); 130 | String stampArcKey = IDoCAdapterUtils.getProperty("stamp_r_arc_key"); 131 | if (stampArcKey == null ||!stampArcKey.equals("false")) { 132 | // stamp the ARCKEY for incoming messages as well so that there is a way to 133 | // correlate IDocs at the adapter level if required. Note that most of the R/* 134 | // systems by default won't add an ARCKEY for the document that they post 135 | // because of the performance facts. Adapter will only stamps an ARCKEY if 136 | // it's missing from the control segment. 137 | IDoCAdapterUtils.stampArcKey(envelope.getBody().getFirstElement(), 138 | msgContext.getMessageID()); 139 | } 140 | 141 | //pass the constructed IDOC message through Axis engine 142 | AxisEngine.receive(msgContext); 143 | 144 | } catch (Exception e) { 145 | log.error("Error while processing the IDoc through the Axis engine", e); 146 | } finally { 147 | IOUtils.closeQuietly(osw); 148 | IOUtils.closeQuietly(baos); 149 | IOUtils.closeQuietly(bais); 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/SAPEndpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap; 21 | 22 | import org.apache.axis2.transport.base.ProtocolEndpoint; 23 | import org.apache.axis2.transport.base.ParamUtils; 24 | import org.apache.axis2.transport.base.threads.WorkerPool; 25 | import org.apache.axis2.addressing.EndpointReference; 26 | import org.apache.axis2.AxisFault; 27 | import org.apache.axis2.description.ParameterInclude; 28 | import org.apache.axis2.description.AxisService; 29 | import org.apache.commons.logging.Log; 30 | import org.apache.commons.logging.LogFactory; 31 | import com.sap.conn.jco.server.JCoServerErrorListener; 32 | import com.sap.conn.jco.server.JCoServerExceptionListener; 33 | import com.sap.conn.jco.server.JCoServerTIDHandler; 34 | import com.sap.conn.jco.server.JCoServer; 35 | import com.sap.conn.jco.server.JCoServerState; 36 | 37 | import java.util.Objects; 38 | import java.util.concurrent.TimeUnit; 39 | 40 | /** 41 | * SAPEndpoint describes a SAP addresible endpoint, the properties decscribe by the 42 | * endpoint are SAP server specifi. The full list of properties can be found at 43 | * http://help.sap.com/saphelp_xmii115/helpdata/en/System_Management/iDoc_Configuration_Editor.htm 44 | */ 45 | public abstract class SAPEndpoint extends ProtocolEndpoint { 46 | 47 | protected static Log log; 48 | 49 | /** 50 | * The sap server name that the endpoint will be connecting to 51 | */ 52 | protected String serverName; 53 | 54 | /** 55 | * Is the error handler for this endpoint is enabled ? 56 | */ 57 | protected boolean errorListenerEnabled; 58 | 59 | /** 60 | * Is this a tx session ? 61 | */ 62 | protected boolean tidHandlerEnabled; 63 | 64 | /** 65 | * The no. of concurrent connections to R/* system 66 | */ 67 | protected int connections; 68 | 69 | /** 70 | * Custom error handler 71 | */ 72 | protected String customErrorListener; 73 | 74 | /** 75 | * Custom exception handler 76 | */ 77 | protected String customExceptionListener; 78 | 79 | /** 80 | * Custom tx handler 81 | */ 82 | protected String customTIDHandler; 83 | 84 | /** 85 | * Max timeout to allow server to stop 86 | */ 87 | protected static int serverStopTimeout = 30000; 88 | 89 | /** 90 | * Waiting time until a response comes. 91 | */ 92 | private int responseTimeout = 120000; 93 | 94 | /** 95 | * Synchronize BAPI Listener 96 | */ 97 | private boolean syncBapiListener = false; 98 | 99 | public SAPEndpoint() { 100 | log = LogFactory.getLog(getClass()); 101 | } 102 | 103 | public EndpointReference[] getEndpointReferences(AxisService service, String ip) throws AxisFault { 104 | return null; 105 | } 106 | 107 | /** 108 | * Load the SAP configuration 109 | * @param params the transport level params 110 | * @return properties loaded sucessfully ? 111 | * @throws AxisFault throws in case of an error 112 | */ 113 | public boolean loadConfiguration(ParameterInclude params) throws AxisFault { 114 | if (!(params instanceof AxisService)) { 115 | return false; 116 | } 117 | 118 | if (log.isDebugEnabled()) { 119 | log.debug("Loading configuration for the SAP endpoint"); 120 | } 121 | 122 | serverName = ParamUtils.getOptionalParam(params, SAPConstants.SERVER_NAME_PARAM); 123 | if (serverName == null) { 124 | return false; 125 | } 126 | 127 | connections = ParamUtils.getOptionalParamInt(params, 128 | SAPConstants.CONNECTIONS_PARAM, 129 | SAPConstants.SAP_SERVER_DEFAULT_CONNECTIONS); 130 | errorListenerEnabled = SAPConstants.SAP_ENABLED.equals( 131 | ParamUtils.getOptionalParam(params, SAPConstants.ENABLE_ERROR_LISTENER_PARAM)); 132 | tidHandlerEnabled = SAPConstants.SAP_ENABLED.equals( 133 | ParamUtils.getOptionalParam(params, SAPConstants.ENABLE_TID_HANDLER_PARAM)); 134 | customErrorListener = ParamUtils.getOptionalParam(params, 135 | SAPConstants.CUSTOM_ERROR_LISTENER_PARAM); 136 | customExceptionListener = ParamUtils.getOptionalParam(params, 137 | SAPConstants.CUSTOM_EXCEPTION_LISTENER_PARAM); 138 | customTIDHandler = ParamUtils.getOptionalParam(params, 139 | SAPConstants.CUSTOM_TID_HANDLER_PARAM); 140 | syncBapiListener = SAPConstants.SAP_ENABLED.equals( 141 | ParamUtils.getOptionalParam(params, SAPConstants.ENABLE_SYNC_BAPI_LISTENER)); 142 | String waitForResponseTimeStr = ParamUtils.getOptionalParam(params, SAPConstants.RESPONSE_TIMEOUT); 143 | try { 144 | if (Objects.nonNull(waitForResponseTimeStr)) { 145 | responseTimeout = Integer.parseInt(waitForResponseTimeStr); 146 | } 147 | } catch (NumberFormatException e) { 148 | log.info("Cannot parse the value " + waitForResponseTimeStr + " in " + SAPConstants.RESPONSE_TIMEOUT 149 | + " into an Integer. Hence, using the default value " + responseTimeout + " for " 150 | + SAPConstants.RESPONSE_TIMEOUT); 151 | } 152 | return true; 153 | } 154 | 155 | protected void handleException(String msg, Exception e) throws AxisFault { 156 | log.error(msg, e); 157 | throw new AxisFault(msg, e); 158 | } 159 | 160 | 161 | /** 162 | * Set up option feature 163 | * @param server the jco server 164 | * @throws Exception throws in case of an error 165 | */ 166 | protected void setupOptionalFeatures(JCoServer server) throws Exception { 167 | server.setConnectionCount(connections); 168 | 169 | if (errorListenerEnabled) { 170 | DefaultErrorListener errorListener = new DefaultErrorListener(); 171 | if (customErrorListener == null) { 172 | if (log.isDebugEnabled()) { 173 | log.debug("Engaging the default error listener for : " + serverName); 174 | } 175 | server.addServerErrorListener(errorListener); 176 | } 177 | if (customExceptionListener == null) { 178 | if (log.isDebugEnabled()) { 179 | log.debug("Engaging the default exception listener for : " + serverName); 180 | } 181 | server.addServerExceptionListener(errorListener); 182 | } 183 | } 184 | 185 | if (tidHandlerEnabled && customTIDHandler == null) { 186 | if (log.isDebugEnabled()) { 187 | log.debug("Engaging the default TID handler for : " + serverName); 188 | } 189 | server.setTIDHandler(new DefaultTIDHandler()); 190 | } 191 | 192 | if (customErrorListener != null) { 193 | Class clazz = this.getClass().getClassLoader().loadClass(customErrorListener); 194 | server.addServerErrorListener((JCoServerErrorListener) clazz.newInstance()); 195 | } 196 | 197 | if (customExceptionListener != null) { 198 | Class clazz = this.getClass().getClassLoader().loadClass(customExceptionListener); 199 | server.addServerExceptionListener((JCoServerExceptionListener) clazz.newInstance()); 200 | } 201 | 202 | if (customTIDHandler != null) { 203 | Class clazz = this.getClass().getClassLoader().loadClass(customTIDHandler); 204 | server.setTIDHandler((JCoServerTIDHandler) clazz.newInstance()); 205 | } 206 | } 207 | 208 | /** 209 | * Block until server state is stopped or maximum timeout reached. 210 | * 211 | * @param server jco server for which we wait to be stopped. 212 | * @return true if the server is stopped before the timeout is exceeded 213 | */ 214 | protected boolean waitForServerStop(JCoServer server) { 215 | long timeStamp = System.currentTimeMillis(); 216 | while (server.getState() != JCoServerState.STOPPED 217 | && timeStamp + serverStopTimeout > System.currentTimeMillis()) { 218 | if (log.isDebugEnabled()) { 219 | log.debug("Waiting for server to stop..."); 220 | } 221 | try { 222 | TimeUnit.SECONDS.sleep(1); 223 | } catch (InterruptedException e) { 224 | //do nothing, just continue the loop 225 | } 226 | } 227 | 228 | if (server.getState() == JCoServerState.STOPPED) { 229 | return true; 230 | } else { 231 | return false; 232 | } 233 | } 234 | 235 | /** 236 | * Checks if the BAPI listener is set to synchronous mode. 237 | * @return true if the synchronous BAPI listener is enabled. 238 | */ 239 | public boolean isSyncBapiListener() { 240 | return syncBapiListener; 241 | } 242 | 243 | public abstract void startEndpoint(WorkerPool workerPool) throws AxisFault; 244 | public abstract void stopEndpoint(); 245 | 246 | public int getResponseTimeout() { 247 | return responseTimeout; 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | org.wso2 22 | wso2 23 | 1.2 24 | 25 | org.wso2.carbon.mediation 26 | 27 | 4.0.0 28 | sap-adapter 29 | 4.7.37-SNAPSHOT 30 | pom 31 | 32 | 33 | https://github.com/wso2/sap-adapter.git 34 | scm:git:https://github.com/wso2/sap-adapter.git 35 | 36 | scm:git:https://github.com/wso2/sap-adapter.git 37 | HEAD 38 | 39 | 40 | 41 | components/sap 42 | features/org.wso2.carbon.sap.feature 43 | 44 | 45 | 46 | 47 | wso2.releases 48 | WSO2 internal Repository 49 | https://maven.wso2.org/nexus/content/repositories/releases/ 50 | 51 | true 52 | daily 53 | ignore 54 | 55 | 56 | 57 | wso2.snapshots 58 | Apache Snapshot Repository 59 | https://maven.wso2.org/nexus/content/repositories/snapshots/ 60 | 61 | true 62 | daily 63 | 64 | 65 | false 66 | 67 | 68 | 69 | wso2-nexus 70 | WSO2 internal Repository 71 | https://maven.wso2.org/nexus/content/groups/wso2-public/ 72 | 73 | true 74 | daily 75 | ignore 76 | 77 | 78 | 79 | 80 | 81 | 82 | wso2-nexus 83 | WSO2 internal Repository 84 | https://maven.wso2.org/nexus/content/groups/wso2-public/ 85 | 86 | true 87 | daily 88 | ignore 89 | 90 | 91 | 92 | wso2.releases 93 | WSO2 internal Repository 94 | https://maven.wso2.org/nexus/content/repositories/releases/ 95 | 96 | true 97 | daily 98 | ignore 99 | 100 | 101 | 102 | wso2.snapshots 103 | Apache Snapshot Repository 104 | https://maven.wso2.org/nexus/content/repositories/snapshots/ 105 | 106 | true 107 | daily 108 | 109 | 110 | false 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | org.wso2.carbon.mediation 119 | org.wso2.carbon.transports.sap 120 | ${project.version} 121 | 122 | 123 | org.apache.axis2.wso2 124 | axis2 125 | ${orbit.version.axis2} 126 | 127 | 128 | org.ops4j.pax.logging 129 | pax-logging-api 130 | ${pax.logging.api.version} 131 | 132 | 133 | org.apache.axis2 134 | axis2-transport-base 135 | ${axis2-transports.base.version} 136 | 137 | 138 | org.apache.ws.commons.axiom 139 | axiom-api 140 | ${axiom.api.wso2.version} 141 | 142 | 143 | 144 | 145 | 146 | 1.4 147 | 2.3.1 148 | 3.2.0 149 | UTF-8 150 | bundle 151 | [1.6.1, 1.7.0) 152 | [1.2.11, 1.3.0) 153 | 1.6.1-wso2v40 154 | 1.10.1 155 | ${orbit.version.axis2} 156 | 1.2.11-wso2v16 157 | ${axiom.version} 158 | 1.5.3 159 | 160 | 161 | 162 | 163 | 164 | 165 | org.apache.felix 166 | maven-bundle-plugin 167 | ${maven.bundle.plugin.version} 168 | true 169 | 170 | NONE 171 | 172 | ${buildNumber} 173 | 174 | 175 | 176 | 177 | org.codehaus.mojo 178 | buildnumber-maven-plugin 179 | ${maven.buildnumber.plugin.version} 180 | 181 | 182 | validate 183 | 184 | create 185 | 186 | 187 | 188 | 189 | false 190 | false 191 | 192 | 193 | 194 | org.apache.maven.plugins 195 | maven-javadoc-plugin 196 | 197 | 1.8 198 | 199 | 200 | 201 | attach-javadocs 202 | 203 | jar 204 | 205 | 206 | 207 | none 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | org.apache.maven.plugins 218 | maven-release-plugin 219 | 220 | clean install 221 | true 222 | 223 | 224 | 225 | org.apache.maven.plugins 226 | maven-deploy-plugin 227 | 228 | 229 | maven-compiler-plugin 230 | ${maven.compiler.plugin.version} 231 | true 232 | 233 | UTF-8 234 | 1.8 235 | 1.8 236 | 237 | 238 | 239 | org.codehaus.mojo 240 | buildnumber-maven-plugin 241 | 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/bapi/Axis2RFCHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap.bapi; 21 | 22 | import com.sap.conn.jco.JCoField; 23 | import com.sap.conn.jco.JCoFieldIterator; 24 | import com.sap.conn.jco.JCoRecord; 25 | import com.sap.conn.jco.JCoRuntimeException; 26 | import com.sap.conn.jco.JCoStructure; 27 | import com.sap.conn.jco.JCoTable; 28 | import com.sap.conn.jco.server.JCoServerFunctionHandler; 29 | import com.sap.conn.jco.server.JCoServerContext; 30 | import com.sap.conn.jco.JCoFunction; 31 | import com.sap.conn.jco.AbapException; 32 | import com.sap.conn.jco.AbapClassException; 33 | import org.apache.axis2.AxisFault; 34 | import org.apache.axiom.om.OMElement; 35 | import org.apache.axis2.Constants; 36 | import org.apache.commons.logging.Log; 37 | import org.apache.commons.logging.LogFactory; 38 | import org.apache.axis2.transport.base.threads.WorkerPool; 39 | import org.apache.axis2.transport.TransportUtils; 40 | import org.apache.axis2.context.MessageContext; 41 | import org.apache.axis2.engine.AxisEngine; 42 | import org.apache.axiom.soap.SOAPEnvelope; 43 | import org.wso2.carbon.transports.sap.SAPConstants; 44 | import org.wso2.carbon.transports.sap.bapi.util.RFCConstants; 45 | 46 | import java.io.ByteArrayInputStream; 47 | import java.util.Iterator; 48 | import java.util.Objects; 49 | 50 | /** 51 | * This class handles BAPI calls returned from the SAP gateway. 52 | *

53 | * This class encapsulates workers for each bapi/rfc request recieved. Workers are responsible for 54 | * handling and converting them to SOAP format 55 | *

56 | */ 57 | public class Axis2RFCHandler implements JCoServerFunctionHandler { 58 | 59 | private static final Log log = LogFactory.getLog(Axis2RFCHandler.class); 60 | 61 | private WorkerPool workerPool; 62 | private BAPIEndpoint endpoint; 63 | 64 | public Axis2RFCHandler(BAPIEndpoint endpoint, WorkerPool workerPool) { 65 | this.endpoint = endpoint; 66 | this.workerPool = workerPool; 67 | } 68 | 69 | /** 70 | * handle bapi requests coming through SAP gateway 71 | * 72 | * @param jCoServerContext JCO Server environment configuration 73 | * @param jCoFunction bAPI/rfc function being called 74 | * @throws AbapException 75 | * @throws AbapClassException 76 | */ 77 | public void handleRequest(JCoServerContext jCoServerContext, 78 | JCoFunction jCoFunction) throws AbapException, AbapClassException { 79 | 80 | if (log.isDebugEnabled()) { 81 | log.debug("New BAPI function call received"); 82 | } 83 | String xml = jCoFunction.toXML(); 84 | SAPOutTransportInfo bapiOutTransportInfo = new SAPOutTransportInfo(); 85 | bapiOutTransportInfo.setProtocol(SAPConstants.SAP_BAPI_PROTOCOL_NAME); 86 | workerPool.execute(new BAPIWorker(xml, jCoFunction, bapiOutTransportInfo)); 87 | if (endpoint.isSyncBapiListener()) { 88 | try { 89 | synchronized (bapiOutTransportInfo) { 90 | int responseTimeout = endpoint.getResponseTimeout(); 91 | if (log.isDebugEnabled()){ 92 | log.debug(" Waiting for response for JCoFunction: " + jCoFunction.toXML() + " with response " 93 | + "timeout: " + responseTimeout); 94 | } 95 | bapiOutTransportInfo.wait(responseTimeout); 96 | 97 | if (Objects.isNull(jCoFunction.getExportParameterList())) { 98 | log.error("Trying to respond to a function that has no Export parameters defined" 99 | + " in the function definition"); 100 | return; 101 | } 102 | OMElement payload = bapiOutTransportInfo.getPayload(); 103 | 104 | // check whether the payload is null, this means that the payload has not been set from the 105 | // mediation sequence. 106 | if (Objects.isNull(payload)) { 107 | JCoStructure returnStructure = null; 108 | try { 109 | returnStructure = jCoFunction.getExportParameterList().getStructure("RETURN"); 110 | } catch (JCoRuntimeException exp) { 111 | if (!(exp.getKey().equals("JCO_ERROR_FIELD_NOT_FOUND"))) { 112 | log.warn("Error retrieving RETURN structure", exp); 113 | return; 114 | } 115 | } 116 | 117 | if (returnStructure != null) { 118 | 119 | JCoFieldIterator e = returnStructure.getFieldIterator(); 120 | while (e.hasNextField()) { 121 | JCoField field = e.nextField(); 122 | if ("TYPE".equals(field.getName())) { 123 | field.setValue("E"); 124 | } else if ("MESSAGE".equals(field.getName())) { 125 | field.setValue("Did not receive a response within " + responseTimeout + "ms"); 126 | } 127 | } 128 | jCoFunction.getExportParameterList().setValue("RETURN", returnStructure); 129 | } else { 130 | if (log.isDebugEnabled()){ 131 | log.debug("RETURN structure is NULL"); 132 | } 133 | } 134 | return; 135 | } 136 | OMElement exportParameters = payload.getFirstElement(); 137 | if (log.isDebugEnabled()){ 138 | log.debug("Setting payload: " + payload + " to the Export " 139 | + "parameters: " + jCoFunction.getExportParameterList()); 140 | } 141 | boolean found = false; 142 | if (Objects.isNull(exportParameters)){ 143 | log.error("Payload with no root element found. A response will not be sent back."); 144 | } 145 | if (exportParameters.getLocalName().equals("BAPI_EXPORT")) { 146 | Iterator childElements = exportParameters.getChildElements(); 147 | if (Objects.isNull(childElements)) { 148 | log.warn("No children elements found under \"BAPI_EXPORT\" element!"); 149 | return; 150 | } 151 | while (childElements.hasNext()) { 152 | found = true; 153 | OMElement childElement = (OMElement) childElements.next(); 154 | String qName = getQname(childElement); 155 | String name = getElementName(childElement); 156 | switch (qName) { 157 | case RFCConstants.STRUCTURE: 158 | JCoStructure structure = processStructure(childElement, jCoFunction, name); 159 | jCoFunction.getExportParameterList().setValue(name, structure); 160 | break; 161 | case RFCConstants.TABLE: 162 | JCoTable table = processTable(childElement, jCoFunction, name); 163 | jCoFunction.getExportParameterList().setValue(name,table); 164 | break; 165 | case RFCConstants.FIELD: 166 | String fieldValue = getFieldValue(childElement, name); 167 | jCoFunction.getExportParameterList().setValue(name, fieldValue); 168 | break; 169 | default: 170 | log.warn("Unknown meta data type tag :" + qName + " detected. " + 171 | "This meta data element will be discarded!"); 172 | } 173 | } 174 | if (!found){ 175 | log.warn("No children elements found under 'BAPI_EXPORT'. A response will not be sent back."); 176 | } 177 | } else { 178 | log.error("Invalid root element found: '" + exportParameters.getLocalName() + "' " 179 | + "instead of mandatory element: 'BAPI_EXPORT'. A response will not be sent back.'"); 180 | } 181 | } 182 | } catch (Throwable e) { 183 | log.error("Error while processing request. A response will not be sent back.", e); 184 | } 185 | } 186 | } 187 | 188 | public static String getQname(OMElement childElement) { 189 | return childElement.getQName().toString(); 190 | } 191 | 192 | private static JCoTable processTable(OMElement element, JCoFunction function, String tableName) 193 | throws AxisFault { 194 | JCoTable table = function.getExportParameterList().getTable(tableName); 195 | if (table == null) { 196 | log.error("Didn't find the specified table : " + tableName + " on the RFC" + 197 | " repository. This table will be ignored"); 198 | } 199 | processTable(element, table); 200 | return table; 201 | } 202 | 203 | private static void processTable(OMElement element, JCoTable jcoTable) throws AxisFault { 204 | Iterator itr = element.getChildElements(); 205 | boolean found = false; 206 | while (itr.hasNext()) { 207 | found = true; 208 | OMElement childElement = (OMElement) itr.next(); 209 | String qName = getQname(childElement); 210 | if (qName.equals("row")) { 211 | String id = childElement.getAttributeValue(RFCConstants.ID_Q); 212 | processRow(childElement, jcoTable, id); 213 | } else { 214 | log.warn("Invalid meta data type element found : " + qName + " .This meta data " + 215 | "type will be ignored"); 216 | } 217 | } 218 | if (!found){ 219 | log.warn("Table: " + element.getLocalName() + " with no children found. This table will be ignored."); 220 | } 221 | } 222 | 223 | private static JCoStructure processStructure(OMElement element, JCoFunction function, String structName) 224 | throws AxisFault { 225 | JCoStructure jCoStructure = function.getExportParameterList().getStructure(structName); 226 | if (jCoStructure != null) { 227 | processRecord(element, jCoStructure); 228 | } else { 229 | log.error("Didn't find the specified structure : " + structName + " on the RFC" + 230 | " repository. This structure will be ignored"); 231 | } 232 | return jCoStructure; 233 | } 234 | 235 | private static void processStructure(OMElement element, JCoStructure jCoStructure, String structName) 236 | throws AxisFault { 237 | if (jCoStructure != null) { 238 | processRecord(element, jCoStructure); 239 | } else { 240 | log.error("Didn't find the specified structure : " + structName + " on the RFC" 241 | + " repository. This structure will be ignored"); 242 | } 243 | } 244 | 245 | public static String getElementName(OMElement element) throws AxisFault { 246 | String name = element.getAttributeValue(RFCConstants.NAME_Q); 247 | if (name == null) { 248 | String message = "Attribute value of qualified name : 'name' for the element: '" + element.getLocalName() 249 | + "' is null. A response will not be sent back"; 250 | throw new AxisFault(message); 251 | } 252 | return name; 253 | } 254 | 255 | 256 | private static void processRow(OMElement element, JCoTable table, String id) throws AxisFault { 257 | int rowId; 258 | try { 259 | rowId = Integer.parseInt(id); 260 | } catch (NumberFormatException ex) { 261 | log.warn("Row ID should be an integer, found " + id + ". Skipping row", ex); 262 | return; 263 | } 264 | 265 | if (table.getNumRows() <= rowId) { 266 | //which mean this is a new row 267 | table.appendRow(); 268 | } else { 269 | //handle existing row 270 | table.setRow(rowId); 271 | } 272 | 273 | processRecord(element, table); 274 | 275 | } 276 | 277 | private static void processRecord(OMElement element, JCoRecord record) throws AxisFault { 278 | Iterator itr = element.getChildElements(); 279 | boolean found = false; 280 | while (itr.hasNext()) { 281 | found = true; 282 | OMElement childElement = (OMElement) itr.next(); 283 | String qName = getQname(childElement); 284 | switch (qName) { 285 | case RFCConstants.STRUCTURE: 286 | String structureName = getElementName(childElement); 287 | processStructure(childElement, record.getStructure(structureName), structureName); 288 | break; 289 | case RFCConstants.TABLE: 290 | String tableName = getElementName(childElement); 291 | processTable(childElement, record.getTable(tableName)); 292 | break; 293 | case RFCConstants.FIELD: 294 | processField(childElement, record); 295 | break; 296 | default: 297 | log.warn("Invalid meta data type element found : " + qName + " .This meta data " + 298 | "type will be ignored"); 299 | } 300 | } 301 | if (!found){ 302 | log.warn("Record: " + element.getLocalName() + " with no children found. This record will be ignored."); 303 | } 304 | } 305 | 306 | public static String getFieldValue(OMElement element, String name) throws AxisFault { 307 | String fieldValue = element.getText(); 308 | if (fieldValue != null) { 309 | return fieldValue; 310 | } else { 311 | return ""; 312 | } 313 | } 314 | 315 | private static void processField(OMElement element, JCoRecord record) throws AxisFault { 316 | String fieldName = element.getAttributeValue(RFCConstants.NAME_Q); 317 | if (fieldName == null) { 318 | throw new AxisFault("A field should have a name. A response will not be sent back"); 319 | } 320 | String fieldValue = getFieldValue(element, fieldName); 321 | record.setValue(fieldName, fieldValue); 322 | } 323 | 324 | private class BAPIWorker implements Runnable { 325 | 326 | private JCoFunction function; 327 | private String xmlContent; 328 | private SAPOutTransportInfo bapiOutTransportInfo; 329 | 330 | BAPIWorker(String xmlContent, JCoFunction function, SAPOutTransportInfo bapiOutTransportInfo) { 331 | this.xmlContent = xmlContent; 332 | this.function = function; 333 | this.bapiOutTransportInfo = bapiOutTransportInfo; 334 | } 335 | 336 | BAPIWorker(String xmlContent) { 337 | this.xmlContent = xmlContent; 338 | } 339 | 340 | public void run() { 341 | if (log.isDebugEnabled()) { 342 | log.debug("Starting a new BAPI worker thread to process the incoming request"); 343 | } 344 | try (ByteArrayInputStream bais = new ByteArrayInputStream(this.xmlContent.getBytes())){ 345 | MessageContext msgContext = endpoint.createMessageContext(); 346 | msgContext.setIncomingTransportName(SAPConstants.SAP_BAPI_PROTOCOL_NAME); 347 | if (log.isDebugEnabled()) { 348 | log.debug("Creating SOAP envelope from the BAPI function call"); 349 | } 350 | SOAPEnvelope envelope = TransportUtils.createSOAPMessage(msgContext, bais, 351 | SAPConstants.SAP_CONTENT_TYPE); 352 | msgContext.setEnvelope(envelope); 353 | msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, bapiOutTransportInfo); 354 | //pass the constructed BAPI message through Axis engine 355 | AxisEngine.receive(msgContext); 356 | } catch (Exception e) { 357 | log.error("Error while processing the BAPI call through the Axis engine", e); 358 | } 359 | } 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/bapi/util/RFCMetaDataParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright WSO2, Inc. (http://wso2.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.wso2.carbon.transports.sap.bapi.util; 17 | 18 | import com.sap.conn.jco.JCoField; 19 | import com.sap.conn.jco.JCoFunction; 20 | import com.sap.conn.jco.JCoStructure; 21 | import com.sap.conn.jco.JCoTable; 22 | import org.apache.axiom.om.OMElement; 23 | import org.apache.axis2.AxisFault; 24 | import org.apache.commons.logging.Log; 25 | import org.apache.commons.logging.LogFactory; 26 | 27 | import java.util.Iterator; 28 | 29 | /** 30 | * This will contain the required methods that can be use to parser a 31 | * meta data description of a BAPI/RFC call. 32 | * So the BNF grammer for the meta data would looks like : 33 | * bapirfc -> import | tables | both 34 | * import -> structure | field | tables | all 35 | * structure -> 1 or more fields 36 | * tables -> 1 or more table 37 | * table -> row 38 | * row -> 1 or more fields 39 | * field -> name and value 40 | *

41 | * See resources folder for sample meta data description files 42 | */ 43 | public class RFCMetaDataParser { 44 | 45 | private static Log log = LogFactory.getLog(RFCMetaDataParser.class); 46 | 47 | /** 48 | * Start processing of the document 49 | * 50 | * @param document the document node 51 | * @param function the RFC function to execute 52 | * @throws AxisFault throws in case of an error 53 | */ 54 | public static void processMetaDataDocument(OMElement document, JCoFunction function) throws AxisFault { 55 | Iterator itr = document.getChildElements(); 56 | while (itr.hasNext()) { 57 | OMElement childElement = (OMElement) itr.next(); 58 | processElement(childElement, function); 59 | } 60 | if (log.isDebugEnabled()) { 61 | log.debug("Processed metadata document"); 62 | } 63 | } 64 | 65 | /** 66 | * Returns the BAPI/RFC function name. 67 | * 68 | * @param rootElement root document element 69 | * @return the BAPI/RFC function name 70 | * @throws AxisFault throws in case of an error 71 | */ 72 | public static String getBAPIRFCFunctionName(OMElement rootElement) throws AxisFault { 73 | if (rootElement != null) { 74 | String rfcFunctionName = rootElement.getAttributeValue(RFCConstants.NAME_Q); 75 | if (rfcFunctionName != null) { 76 | return rfcFunctionName; 77 | } else { 78 | throw new AxisFault("BAPI/RFC function name is mandatory in meta data configuration"); 79 | } 80 | } else { 81 | throw new AxisFault("Invalid meta data root element.Found: " + rootElement + "" + 82 | ". Required:" + RFCConstants.BAPIRFC); 83 | } 84 | } 85 | 86 | private static void processElement(OMElement element, JCoFunction function) throws AxisFault { 87 | String qname = element.getQName().toString(); 88 | if (qname != null) { 89 | if (qname.equals("import")) { 90 | processImport(element, function); 91 | } else if (qname.equals("tables")) { 92 | processTables(element, function); 93 | } else { 94 | log.warn("Unknown meta data type tag :" + qname + " detected. " + 95 | "This meta data element will be discarded!"); 96 | } 97 | } 98 | } 99 | 100 | private static void processImport(OMElement element, JCoFunction function) throws AxisFault { 101 | Iterator itr = element.getChildElements(); 102 | while (itr.hasNext()) { 103 | OMElement childElement = (OMElement) itr.next(); 104 | String qname = childElement.getQName().toString(); 105 | String name = childElement.getAttributeValue(RFCConstants.NAME_Q); 106 | if (qname.equals("structure")) { 107 | processStructure(childElement, function, name); 108 | } else if (qname.equals("field")) { 109 | processField(childElement, function, name); 110 | } else if (qname.equals("tables")) { 111 | processTablesParameter(childElement, function); 112 | } else { 113 | log.warn("Unknown meta data type tag :" + qname + " detected. " + 114 | "This meta data element will be discarded!"); 115 | } 116 | } 117 | } 118 | 119 | private static void processStructure(OMElement element, JCoFunction function, String strcutName) 120 | throws AxisFault { 121 | if (strcutName == null) { 122 | throw new AxisFault("A structure should have a name!"); 123 | } 124 | JCoStructure jcoStrcture = function.getImportParameterList().getStructure(strcutName); 125 | if (jcoStrcture != null) { 126 | Iterator itr = element.getChildElements(); 127 | boolean isRecordFound = false; 128 | while (itr.hasNext()) { 129 | OMElement childElement = (OMElement) itr.next(); 130 | String qname = childElement.getQName().toString(); 131 | if (qname.equals("field")) { 132 | String fieldName = childElement.getAttributeValue(RFCConstants.NAME_Q); 133 | String fieldValue = childElement.getText(); 134 | for (JCoField field : jcoStrcture) { 135 | if (fieldName != null && fieldName.equals(field.getName())) { 136 | isRecordFound = true; 137 | field.setValue(fieldValue); // TODO - may be we need to check for null ? 138 | break; 139 | } 140 | } 141 | if (!isRecordFound) { 142 | throw new AxisFault("Invalid configuration! The field : " + fieldName + "" + 143 | " did not find the the strcture : " + strcutName); 144 | } 145 | } else if (qname.equals("structure")) { 146 | String structureName = childElement.getAttributeValue(RFCConstants.NAME_Q); 147 | JCoStructure jcoStructureInner = jcoStrcture.getStructure(structureName); 148 | 149 | if (jcoStructureInner == null) { 150 | throw new AxisFault("Invalid configuration! The structure : " + structureName + "" 151 | + " did not find the the strcture : " + strcutName); 152 | } 153 | 154 | processStructure(childElement, jcoStructureInner, structureName); 155 | } else if (qname.equals("table")) { 156 | String name = childElement.getAttributeValue(RFCConstants.NAME_Q); 157 | JCoTable jcoTableInner = jcoStrcture.getTable(name); 158 | processTable(childElement, jcoTableInner); 159 | } else { 160 | log.warn("Invalid meta data type element found : " + qname + " .This meta data " + 161 | "type will be ignored"); 162 | } 163 | } 164 | } else { 165 | log.error("Didn't find the specified structure : " + strcutName + " on the RFC" + 166 | " repository. This structure will be ignored"); 167 | } 168 | } 169 | 170 | private static void processStructure(OMElement element, JCoStructure jcoStrcture, String strcutName) 171 | throws AxisFault { 172 | if (jcoStrcture != null) { 173 | Iterator itr = element.getChildElements(); 174 | boolean isRecordFound = false; 175 | while (itr.hasNext()) { 176 | OMElement childElement = (OMElement) itr.next(); 177 | String qname = childElement.getQName().toString(); 178 | if (qname.equals("field")) { 179 | String fieldName = childElement.getAttributeValue(RFCConstants.NAME_Q); 180 | String fieldValue = childElement.getText(); 181 | for (JCoField field : jcoStrcture) { 182 | if (fieldName != null && fieldName.equals(field.getName())) { 183 | isRecordFound = true; 184 | field.setValue(fieldValue); // TODO - may be we need to check for null ? 185 | break; 186 | } 187 | } 188 | if (!isRecordFound) { 189 | throw new AxisFault("Invalid configuration! The field : " + fieldName + "" 190 | + " did not find the the strcture : " + strcutName); 191 | } 192 | } else if (qname.equals("structure")) { 193 | String structureName = childElement.getAttributeValue(RFCConstants.NAME_Q); 194 | JCoStructure jcoStructureInner = jcoStrcture.getStructure(structureName); 195 | 196 | if (jcoStructureInner == null) { 197 | throw new AxisFault("Invalid configuration! The structure : " + structureName + "" 198 | + " did not find the the strcture : " + strcutName); 199 | } 200 | 201 | processStructure(childElement, jcoStructureInner, structureName); 202 | } else if (qname.equals("table")) { 203 | String tableName = childElement.getAttributeValue(RFCConstants.NAME_Q); 204 | JCoTable jcoTableInner = jcoStrcture.getTable(tableName); 205 | 206 | if (jcoTableInner == null) { 207 | throw new AxisFault("Invalid configuration! The table : " + tableName + "" 208 | + " did not find the the strcture : " + strcutName); 209 | } 210 | 211 | processTable(childElement, jcoTableInner); 212 | } else { 213 | log.warn("Invalid meta data type element found : " + qname + " .This meta data " 214 | + "type will be ignored"); 215 | } 216 | } 217 | } else { 218 | log.error("Didn't find the specified structure : " + strcutName + " on the RFC" 219 | + " repository. This structure will be ignored"); 220 | } 221 | } 222 | 223 | private static void processTable(OMElement element, JCoTable jconTable) 224 | throws AxisFault { 225 | JCoTable inputTable = jconTable; 226 | Iterator itr = element.getChildElements(); 227 | while (itr.hasNext()) { 228 | OMElement childElement = (OMElement) itr.next(); 229 | String qname = childElement.getQName().toString(); 230 | String id = childElement.getAttributeValue(RFCConstants.ID_Q); 231 | if (qname.equals("row")) { 232 | processRow(childElement, inputTable, id); 233 | } 234 | 235 | } 236 | } 237 | 238 | private static void processField(OMElement element, JCoFunction function, String fieldName) 239 | throws AxisFault { 240 | if (fieldName == null) { 241 | throw new AxisFault("A field should have a name!"); 242 | } 243 | String fieldValue = element.getText(); 244 | if (fieldValue != null) { 245 | // TODO-check for avalibility of the field 246 | function.getImportParameterList().setValue(fieldName, fieldValue); 247 | } 248 | } 249 | 250 | public static void processFieldValue(String fieldName, String fieldValue, JCoFunction function) throws AxisFault { 251 | if (fieldValue != null) { 252 | function.getImportParameterList().setValue(fieldName, fieldValue); 253 | } else { 254 | log.warn(fieldName + "is set with an empty value"); 255 | } 256 | } 257 | 258 | private static void processTables(OMElement element, JCoFunction function) throws AxisFault { 259 | Iterator itr = element.getChildElements(); 260 | while (itr.hasNext()) { 261 | OMElement childElement = (OMElement) itr.next(); 262 | String qname = childElement.getQName().toString(); 263 | String tableName = childElement.getAttributeValue(RFCConstants.NAME_Q); 264 | if (qname.equals("table")) { 265 | processTable(childElement, function, tableName); 266 | } else { 267 | log.warn("Invalid meta data type element found : " + qname + " .This meta data " + 268 | "type will be ignored"); 269 | } 270 | } 271 | } 272 | 273 | private static void processTablesParameter(OMElement element, JCoFunction function) throws AxisFault { 274 | Iterator itr = element.getChildElements(); 275 | while (itr.hasNext()) { 276 | OMElement childElement = (OMElement) itr.next(); 277 | String qname = childElement.getQName().toString(); 278 | String tableName = childElement.getAttributeValue(RFCConstants.NAME_Q); 279 | if (qname.equals("table")) { 280 | processTableParameter(childElement, function, tableName); 281 | } else { 282 | log.warn("Invalid meta data type element found : " + qname + " .This meta data " + 283 | "type will be ignored"); 284 | } 285 | } 286 | } 287 | 288 | private static void processTable(OMElement element, JCoFunction function, String tableName) 289 | throws AxisFault { 290 | JCoTable inputTable = function.getTableParameterList().getTable(tableName); 291 | if (inputTable == null) { 292 | throw new AxisFault("Input table :" + tableName + " does not exist"); 293 | } 294 | Iterator itr = element.getChildElements(); 295 | while (itr.hasNext()) { 296 | OMElement childElement = (OMElement) itr.next(); 297 | String qname = childElement.getQName().toString(); 298 | String id = childElement.getAttributeValue(RFCConstants.ID_Q); 299 | if (qname.equals("row")) { 300 | processRow(childElement, inputTable, id); 301 | } else { 302 | log.warn("Invalid meta data type element found : " + qname + " .This meta data " + 303 | "type will be ignored"); 304 | } 305 | 306 | } 307 | } 308 | 309 | private static void processTableParameter(OMElement element, JCoFunction function, String tableName) 310 | throws AxisFault { 311 | JCoTable inputTable = function.getImportParameterList().getTable(tableName); 312 | if (inputTable == null) { 313 | throw new AxisFault("Input table parameter :" + tableName + " does not exist"); 314 | } 315 | Iterator itr = element.getChildElements(); 316 | while (itr.hasNext()) { 317 | OMElement childElement = (OMElement) itr.next(); 318 | String qname = childElement.getQName().toString(); 319 | String id = childElement.getAttributeValue(RFCConstants.ID_Q); 320 | if (qname.equals("row")) { 321 | processRow(childElement, inputTable, id); 322 | } else { 323 | log.warn("Invalid meta data type element found : " + qname + " .This meta data " + 324 | "type will be ignored"); 325 | } 326 | 327 | } 328 | } 329 | 330 | private static void processRow(OMElement element, JCoTable table, String id) 331 | throws AxisFault { 332 | 333 | int rowId; 334 | try { 335 | rowId = Integer.parseInt(id); 336 | } catch (NumberFormatException ex) { 337 | log.warn("Row ID should be a integer, found " + id + ". Skipping row", ex); 338 | return; 339 | } 340 | 341 | if (table.getNumRows() <= rowId) { 342 | //which mean this is a new row 343 | table.appendRow(); 344 | 345 | } else { 346 | //handle existing row 347 | table.setRow(rowId); 348 | } 349 | 350 | Iterator itr = element.getChildElements(); 351 | while (itr.hasNext()) { 352 | OMElement childElement = (OMElement) itr.next(); 353 | String qname = childElement.getQName().toString(); 354 | if (qname != null && qname.equals("field")) { 355 | processField(childElement, table); 356 | } else if (qname != null && qname.equals("structure")) { 357 | String structureName = childElement.getAttributeValue(RFCConstants.NAME_Q); 358 | JCoStructure jcoStrctureInner = table.getStructure(structureName); 359 | 360 | if (jcoStrctureInner != null) { 361 | processStructure(childElement, jcoStrctureInner, structureName); 362 | } else { 363 | log.warn("Invalid meta data type element found : " + structureName + " .This meta data " + 364 | "type will be ignored"); 365 | } 366 | } else if (qname != null && qname.equals("table")) { 367 | String tableName = childElement.getAttributeValue(RFCConstants.NAME_Q); 368 | JCoTable jCoTableInner = table.getTable(tableName); 369 | if (jCoTableInner != null) { 370 | processTable(childElement, jCoTableInner); 371 | } else { 372 | log.warn("Invalid table name found : " + tableName + ". Processing this table will be ignored"); 373 | } 374 | } else { 375 | log.warn("Invalid meta data type element found : " + qname + " .This meta data " + 376 | "type will be ignored"); 377 | } 378 | } 379 | } 380 | 381 | private static void processField(OMElement element, JCoTable table) 382 | throws AxisFault { 383 | String fieldName = element.getAttributeValue(RFCConstants.NAME_Q); 384 | String fieldValue = element.getText(); 385 | 386 | if (fieldName == null) { 387 | throw new AxisFault("A field should have a name!"); 388 | } 389 | if (fieldValue != null) { 390 | table.setValue(fieldName, fieldValue); 391 | } 392 | } 393 | } -------------------------------------------------------------------------------- /components/sap/src/main/java/org/wso2/carbon/transports/sap/SAPTransportSender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | * 18 | */ 19 | 20 | package org.wso2.carbon.transports.sap; 21 | 22 | import com.sap.conn.idoc.IDocDocumentList; 23 | import com.sap.conn.idoc.IDocFactory; 24 | import com.sap.conn.idoc.IDocRepository; 25 | import com.sap.conn.idoc.jco.JCoIDoc; 26 | import com.sap.conn.jco.*; 27 | import com.sap.conn.jco.ext.Environment; 28 | import org.apache.axiom.om.OMElement; 29 | import org.apache.axiom.soap.SOAPEnvelope; 30 | import org.apache.axis2.AxisFault; 31 | import org.apache.axis2.Constants; 32 | import org.apache.axis2.context.ConfigurationContext; 33 | import org.apache.axis2.context.MessageContext; 34 | import org.apache.axis2.description.OutInAxisOperation; 35 | import org.apache.axis2.description.Parameter; 36 | import org.apache.axis2.description.TransportOutDescription; 37 | import org.apache.axis2.engine.AxisEngine; 38 | import org.apache.axis2.transport.OutTransportInfo; 39 | import org.apache.axis2.transport.TransportUtils; 40 | import org.apache.axis2.transport.base.AbstractTransportSender; 41 | import org.apache.axis2.util.MessageContextBuilder; 42 | import org.wso2.carbon.transports.sap.bapi.SAPOutTransportInfo; 43 | import org.wso2.carbon.transports.sap.bapi.util.RFCConstants; 44 | import org.wso2.carbon.transports.sap.bapi.util.RFCMetaDataParser; 45 | import org.wso2.carbon.transports.sap.idoc.DefaultIDocXMLMapper; 46 | import org.wso2.carbon.transports.sap.idoc.IDocXMLMapper; 47 | 48 | import javax.xml.namespace.QName; 49 | import javax.xml.stream.XMLStreamException; 50 | import java.io.ByteArrayInputStream; 51 | import java.net.URI; 52 | import java.util.HashMap; 53 | import java.util.Iterator; 54 | import java.util.Map; 55 | 56 | /** 57 | * SAPTransportSender provides the ransport Sender implementation for SAP endpoints 58 | * 59 | */ 60 | public class SAPTransportSender extends AbstractTransportSender { 61 | 62 | private Map xmlMappers = new HashMap(); 63 | 64 | private IDocXMLMapper defaultMapper = new DefaultIDocXMLMapper(); 65 | 66 | /** 67 | * The SAP endpoint error code 68 | */ 69 | public static final String ERROR_CODE = "ERROR_CODE"; 70 | 71 | /** 72 | * Error while sending the Message through wire 73 | */ 74 | public static final int SAP_TRANSPORT_ERROR = 8000; 75 | 76 | /** 77 | * String constant for the header name wait. 78 | */ 79 | public static final String SAP_WAIT = "sap.wait"; 80 | 81 | /** 82 | * String constant for the header name of sap transaciton id. 83 | */ 84 | public static final String SAP_TRANSACTION_ID = "SAP-Transaction-Id"; 85 | 86 | /** 87 | * SAP destination error. Possibly something wrong with the remote R/* system 88 | */ 89 | public static final int SAP_DESTINATION_ERROR = 8001; 90 | 91 | /** 92 | * Left angle bracket 93 | */ 94 | public static final String LEFT_ANGLE_BRACKET = "<"; 95 | 96 | /** 97 | * Right angle bracket 98 | */ 99 | public static final String RIGHT_ANGLE_BRACKET = ">"; 100 | 101 | /** 102 | * Forward slash 103 | */ 104 | public static final String FORWARD_SLASH = "/"; 105 | 106 | /** 107 | * Opening tag of the aggregated response when transactions are enabled 108 | */ 109 | private static final String AGGREGATED_RESPONSE_OPENING_TAG = LEFT_ANGLE_BRACKET + "BAPI_TRANSACTION" + 110 | RIGHT_ANGLE_BRACKET; 111 | 112 | /** 113 | * Closing tag of the aggregated response when transactions are enabled 114 | */ 115 | private static final String AGGREGATED_RESPONSE_CLOSING_TAG = LEFT_ANGLE_BRACKET + FORWARD_SLASH + 116 | "BAPI_TRANSACTION" + RIGHT_ANGLE_BRACKET; 117 | 118 | /** 119 | * This property allows to sent the original SAP error message without handling at SAP implementation and throwing 120 | * as an AxisFault 121 | */ 122 | private static final String SAP_ESCAPE_ERROR_HANDLING = "sap.escape.error.handling"; 123 | 124 | /** 125 | * The 'WAIT' field name. 126 | */ 127 | public static final String FIELD_NAME_WAIT = "WAIT"; 128 | 129 | @Override 130 | public void init(ConfigurationContext cfgCtx, TransportOutDescription trpOut) throws AxisFault { 131 | super.init(cfgCtx, trpOut); 132 | 133 | CarbonDestinationDataProvider provider = new CarbonDestinationDataProvider(); 134 | if (!Environment.isServerDataProviderRegistered()) { 135 | Environment.registerServerDataProvider(provider); 136 | } 137 | if (!Environment.isDestinationDataProviderRegistered()) { 138 | Environment.registerDestinationDataProvider(provider); 139 | } 140 | //check and initalize if XML mappers are declared 141 | Parameter xmlMappersParam = trpOut.getParameter(SAPConstants.CUSTOM_IDOC_XML_MAPPERS); 142 | if (xmlMappersParam != null) { 143 | OMElement mappersElt = xmlMappersParam.getParameterElement().getFirstElement(); 144 | Iterator mappers = mappersElt.getChildrenWithName(new QName(SAPConstants.XML_MAPPER_ELT)); 145 | try { 146 | while (mappers.hasNext()) { 147 | OMElement m = (OMElement) mappers.next(); 148 | String key = m.getAttributeValue(new QName(SAPConstants.XML_MAPPER_KEY_ATTR)); 149 | String value = m.getText().trim(); 150 | Class clazz = this.getClass().getClassLoader().loadClass(value); 151 | IDocXMLMapper mapper = (IDocXMLMapper) clazz.newInstance(); 152 | 153 | xmlMappers.put(key, mapper); 154 | } 155 | } catch (Exception e) { 156 | throw new AxisFault("Error while initializing the SAP transport sender", e); 157 | } 158 | } 159 | } 160 | 161 | /** 162 | * Send the SAP message to the SAP R/* system, Accepted URL format: idoc:/MyServer[?version=2] 163 | * @param messageContext axis2 message context 164 | * @param targetEPR SAP EPR 165 | * @param outTransportInfo out transport info 166 | * @throws AxisFault throws in case of an error 167 | */ 168 | public void sendMessage(MessageContext messageContext, String targetEPR, 169 | OutTransportInfo outTransportInfo) throws AxisFault { 170 | 171 | if (!messageContext.isServerSide()) { 172 | if (targetEPR == null) { 173 | throw new AxisFault("Cannot send an IDoc without a target SAP EPR"); 174 | } 175 | 176 | try { 177 | URI uri = new URI(targetEPR); 178 | if (log.isDebugEnabled()) { 179 | log.debug("Started sending message to uri=" + uri); 180 | } 181 | String destName = uri.getPath().substring(1); 182 | JCoDestination destination = JCoDestinationManager.getDestination(destName); 183 | if (log.isDebugEnabled()) { 184 | log.debug("Retrieved destination: " + destination.getDestinationID()); 185 | } 186 | if (uri.getScheme().equals(SAPConstants.SAP_IDOC_PROTOCOL_NAME)) { 187 | IDocRepository iDocRepository = JCoIDoc.getIDocRepository(destination); 188 | String tid = destination.createTID(); 189 | if (log.isDebugEnabled()) { 190 | log.debug("Created transaction ID: " + tid); 191 | } 192 | //Set the transaction id as a transport header so that it can be used later. 193 | Object headers = messageContext.getProperty(MessageContext.TRANSPORT_HEADERS); 194 | Map headersMap = (Map) headers; 195 | headersMap.put(SAP_TRANSACTION_ID, tid); 196 | 197 | IDocDocumentList iDocList = getIDocs(messageContext, iDocRepository); 198 | JCoIDoc.send(iDocList, getIDocVersion(uri), destination, tid); 199 | destination.confirmTID(tid); 200 | if (log.isDebugEnabled()) { 201 | log.debug("Successfully sent Idoc. Destination:" + destination.getDestinationID()); 202 | } 203 | } else if (uri.getScheme().equals(SAPConstants.SAP_BAPI_PROTOCOL_NAME)) { 204 | sendBapiRequest(messageContext, destination); 205 | } else { 206 | handleException("Invalid protocol name : " + uri.getScheme() + " in SAP URL"); 207 | } 208 | } catch (Exception e) { 209 | log.error("Error while sending request to the EPR" + targetEPR, e); 210 | sendFault(messageContext,e, SAP_DESTINATION_ERROR); 211 | handleException("Error while sending request to the EPR : " + targetEPR, e); 212 | } 213 | } else { 214 | log.debug("Handling response"); 215 | if (messageContext.getProperty(Constants.OUT_TRANSPORT_INFO) instanceof SAPOutTransportInfo) { 216 | SAPOutTransportInfo outTransportInfoValue = (SAPOutTransportInfo) messageContext 217 | .getProperty(Constants.OUT_TRANSPORT_INFO); 218 | if (outTransportInfoValue.getProtocol().equals(SAPConstants.SAP_BAPI_PROTOCOL_NAME)) { 219 | populateBapiReponse(messageContext, outTransportInfoValue); 220 | } 221 | } 222 | } 223 | } 224 | 225 | /** 226 | * Check the transaction commit property value 227 | * 228 | * @param messageContext axis2 Message Context 229 | * @return true or false based on the value 230 | */ 231 | private boolean isTransaction(MessageContext messageContext) { 232 | String transactionCommit = (String) messageContext.getProperty(SAPConstants.TRANSACTION_COMMIT_PARAM); 233 | return null != transactionCommit && "true".equalsIgnoreCase(transactionCommit); 234 | } 235 | 236 | private char getIDocVersion(URI uri) { 237 | String query = uri.getQuery(); 238 | if (query != null && query.startsWith(SAPConstants.SAP_IDOC_VERSION)) { 239 | String version = query.substring(query.indexOf('=') + 1); 240 | if (SAPConstants.SAP_IDOC_VERSION_2.equals(version)) { 241 | return IDocFactory.IDOC_VERSION_2; 242 | } else if (SAPConstants.SAP_IDOC_VERSION_3.equals(version)) { 243 | return IDocFactory.IDOC_VERSION_3; 244 | } 245 | } 246 | return IDocFactory.IDOC_VERSION_DEFAULT; 247 | } 248 | 249 | /** 250 | * Check the logon property value 251 | * 252 | * @param messageContext axis2 Message Context 253 | * @return true or false based on the value 254 | */ 255 | private boolean isLogon(MessageContext messageContext) { 256 | String logon = (String) messageContext.getProperty(SAPConstants.TRANSACTION_SAP_LOGON); 257 | return (null != logon) && ("true".equalsIgnoreCase(logon)); 258 | } 259 | 260 | private void logon(MessageContext messageContext, JCoDestination destination, String escapeErrorHandling) 261 | throws AxisFault { 262 | JCoFunction logonFunction = getRFCfunction(destination, SAPConstants.BABI_XMI_LOGON); 263 | logonFunction.getImportParameterList().setValue(SAPConstants.EXTCOMPANY, 264 | (String) messageContext.getProperty(SAPConstants.TRANSPORT_SAP_EXTCOMPANY)); 265 | logonFunction.getImportParameterList().setValue(SAPConstants.EXTPRODUCT, 266 | (String) messageContext.getProperty(SAPConstants.TRANSPORT_SAP_EXTPRODUCT)); 267 | logonFunction.getImportParameterList().setValue(SAPConstants.INTERFACE, 268 | (String) messageContext.getProperty(SAPConstants.TRANSPORT_SAP_INTERFACE)); 269 | logonFunction.getImportParameterList().setValue(SAPConstants.VERSION, 270 | (String) messageContext.getProperty(SAPConstants.TRANSPORT_SAP_VERSION)); 271 | String logonResponse = evaluateRFCfunction(logonFunction, destination, escapeErrorHandling); 272 | if (log.isDebugEnabled()) { 273 | log.debug("BAPI XMI Logon response: " + logonResponse); 274 | } 275 | } 276 | 277 | /** 278 | * retrive IDOCs from message context 279 | * 280 | * @param msgContext Synapse Message Context 281 | * @param repo the repository to be used for querying the needed IDoc meta data information in 282 | * order to create the corresponding IDocDocumentList instance 283 | * @return A list of IDOcs 284 | * @throws Exception in case of an error 285 | */ 286 | private IDocDocumentList getIDocs(MessageContext msgContext, 287 | IDocRepository repo) throws Exception { 288 | 289 | Object mapper = msgContext.getOptions().getProperty(SAPConstants.CLIENT_XML_MAPPER_KEY); 290 | //check for any user defined xml mappers 291 | if (mapper != null && xmlMappers.containsKey(mapper.toString())) { 292 | return xmlMappers.get(mapper.toString()).getDocumentList(repo, msgContext); 293 | } else { 294 | return defaultMapper.getDocumentList(repo, msgContext); 295 | } 296 | } 297 | 298 | /** 299 | * Evaluate the BAPI/RFC function in a remote R/* system 300 | * @param function the BAPI/RFC function 301 | * @param destination jco destination 302 | * @return the result of the function execution 303 | * @throws AxisFault throws in case of an error 304 | */ 305 | private String evaluateRFCfunction(JCoFunction function, JCoDestination destination, String escapeErrorHandling) 306 | throws AxisFault { 307 | log.info("Invoking the RFC function :" + function.getName()); 308 | try { 309 | function.execute(destination); 310 | if (log.isDebugEnabled()) { 311 | log.debug("Invoked the RFC function :" + function.getName()); 312 | } 313 | } catch (JCoException e) { 314 | throw new AxisFault("Could not execute the RFC function: " + function, e); 315 | } 316 | 317 | // there seems to be some error that we need to report: TODO ? 318 | //If property "sap.escape.error.handling" is defined and is true, the original SAP exceptions will 319 | // be sent without being handled and thrown as an AxisFault 320 | if (function.getExportParameterList() != null) { 321 | JCoStructure returnStructure = null; 322 | try { 323 | returnStructure = function.getExportParameterList().getStructure("RETURN"); 324 | } catch (JCoRuntimeException e) { 325 | if (!(e.getKey().equals("JCO_ERROR_FIELD_NOT_FOUND"))) { 326 | throw e; 327 | } 328 | } 329 | 330 | if (returnStructure != null) { 331 | String returnStructureStr = returnStructure.toXML(); 332 | 333 | if ("false".equals(escapeErrorHandling)) { 334 | String type = returnStructure.getString("TYPE"); 335 | if (!("S".equals(type) || "I".equals(type) || "W".equals(type) || "".equals(type))) { 336 | throw new AxisFault("Erroneous response while invoking the function: " + function.getName() + 337 | ", of type" + type + " response: " + returnStructureStr); 338 | } 339 | } 340 | } 341 | } 342 | 343 | return function.toXML(); 344 | } 345 | 346 | /** 347 | * Returns the BAPI/RFC function from the SAP repository 348 | * @param destination SAP JCO destination 349 | * @param rfcName the rfc name 350 | * @return the BAPI/RFC function 351 | * @throws AxisFault throws in case of an error 352 | */ 353 | private JCoFunction getRFCfunction(JCoDestination destination, String rfcName) 354 | throws AxisFault { 355 | log.info("Retriving the BAPI/RFC function : " + rfcName + " from the destination : " + 356 | destination); 357 | JCoFunction function = null; 358 | try { 359 | function = destination.getRepository().getFunction(rfcName); 360 | if (log.isDebugEnabled()) { 361 | log.debug("retrieved function: " + function.getName()); 362 | } 363 | } catch (JCoException e) { 364 | throw new AxisFault("RFC function " + function + " could not found in SAP system", e); 365 | } 366 | return function; 367 | } 368 | 369 | 370 | /** 371 | * Process and send the response of the RFC execution through axis engine 372 | * @param msgContext axis2 message context 373 | * @param payLoad RFC execution payload 374 | * @throws AxisFault throws in case of an error 375 | */ 376 | private void processResponse(MessageContext msgContext, String payLoad) 377 | throws AxisFault { 378 | if (log.isDebugEnabled()) { 379 | log.debug("Response received: " + payLoad); 380 | } 381 | if (!(msgContext.getAxisOperation() instanceof OutInAxisOperation)) { 382 | return; 383 | } 384 | try { 385 | MessageContext responseMessageContext = createResponseMessageContext(msgContext); 386 | ByteArrayInputStream bais = new ByteArrayInputStream(payLoad.getBytes()); 387 | SOAPEnvelope envelope = TransportUtils.createSOAPMessage(msgContext, bais, 388 | SAPConstants.SAP_CONTENT_TYPE); 389 | responseMessageContext.setEnvelope(envelope); 390 | AxisEngine.receive(responseMessageContext); 391 | log.info("Sending response out.."); 392 | } catch (XMLStreamException e) { 393 | throw new AxisFault("Error while processing response", e); 394 | } 395 | } 396 | 397 | /** 398 | * Send an axis fault if an error happened 399 | * @param msgContext axis2 message context 400 | * @param e the exception 401 | * @param errorCode error code of the error 402 | */ 403 | private void sendFault(MessageContext msgContext, Exception e , int errorCode) { 404 | //TODO Fix this properly 405 | try { 406 | MessageContext faultContext = MessageContextBuilder.createFaultMessageContext( 407 | msgContext, e); 408 | faultContext.setProperty(ERROR_CODE,errorCode); 409 | faultContext.setProperty("ERROR_MESSAGE",e.getMessage()); 410 | faultContext.setProperty("SENDING_FAULT", Boolean.TRUE); 411 | if (msgContext.getAxisOperation() != null && 412 | msgContext.getAxisOperation().getMessageReceiver() != null) { 413 | msgContext.getAxisOperation().getMessageReceiver().receive(faultContext); 414 | } else { 415 | log.error("Could not create the fault message.", e); 416 | } 417 | } catch (AxisFault axisFault) { 418 | log.fatal("Could not create the fault message.", axisFault); 419 | } 420 | } 421 | 422 | private void sendBapiRequest(MessageContext messageContext, JCoDestination destination) throws AxisFault, 423 | JCoException { 424 | 425 | if (log.isDebugEnabled()) { 426 | log.debug("Invoking BAPI endpoint"); 427 | } 428 | String escapeErrorHandling = (String) messageContext.getProperty(SAP_ESCAPE_ERROR_HANDLING); 429 | boolean isLogon = isLogon(messageContext); 430 | if (log.isDebugEnabled()) { 431 | log.debug("Transaction property :" + messageContext 432 | .getProperty(SAPConstants.TRANSACTION_COMMIT_PARAM)); 433 | log.debug("Logon property :" + messageContext.getProperty(SAPConstants.TRANSACTION_SAP_LOGON)); 434 | } 435 | String rfcFunctionName = null; 436 | try { 437 | OMElement payLoad, body; 438 | body = messageContext.getEnvelope().getBody(); 439 | payLoad = body.getFirstChildWithName(new QName(RFCConstants.BAPIRFC)); 440 | if (log.isDebugEnabled()) { 441 | log.debug("Received RFC/Meta DATA: " + payLoad); 442 | } 443 | rfcFunctionName = RFCMetaDataParser.getBAPIRFCFunctionName(payLoad); 444 | if (isTransaction(messageContext) || isLogon) { 445 | if (log.isDebugEnabled()) { 446 | log.debug("Beginning Transaction for function: " + rfcFunctionName); 447 | } 448 | JCoContext.begin(destination); 449 | if (log.isDebugEnabled()) { 450 | log.debug("Transaction begun. Function: " + rfcFunctionName); 451 | } 452 | } 453 | if (isLogon) { 454 | logon(messageContext, destination, escapeErrorHandling); 455 | } 456 | if (log.isDebugEnabled()) { 457 | log.debug("Looking up the BAPI/RFC function: " + rfcFunctionName 458 | + ". In the meta data repository"); 459 | } 460 | 461 | JCoFunction function = getRFCfunction(destination, rfcFunctionName); 462 | RFCMetaDataParser.processMetaDataDocument(payLoad, function); 463 | String bapiResponse = evaluateRFCfunction(function, destination, escapeErrorHandling); 464 | 465 | if (isTransaction(messageContext)) { 466 | //commit the transaction 467 | JCoFunction commitFunction = getRFCfunction(destination, SAPConstants.BAPI_TRANSACTION_COMMIT); 468 | Object headers = messageContext.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS); 469 | Map headersMap = (Map) headers; 470 | String waitValue = (String) headersMap.get(SAP_WAIT); 471 | if (waitValue != null && !waitValue.isEmpty()) { 472 | RFCMetaDataParser.processFieldValue(FIELD_NAME_WAIT, waitValue, commitFunction); 473 | } 474 | String commitResponse = evaluateRFCfunction(commitFunction, destination, escapeErrorHandling); 475 | // create an aggregated response containing both the BAPI response and the commit response 476 | StringBuilder sb = new StringBuilder(); 477 | 478 | sb.append(AGGREGATED_RESPONSE_OPENING_TAG); 479 | sb.append(bapiResponse).append(commitResponse); 480 | sb.append(AGGREGATED_RESPONSE_CLOSING_TAG); 481 | 482 | bapiResponse = sb.toString(); 483 | if (log.isDebugEnabled()) { 484 | log.debug("Committed transaction. Function: " + rfcFunctionName); 485 | } 486 | 487 | } 488 | processResponse(messageContext, bapiResponse); 489 | } catch (Exception e) { 490 | log.error("Error while sending request", e); 491 | if (isTransaction(messageContext)) { 492 | //Rollback transaction if something goes wrong during the transaction 493 | JCoFunction rollbackFunction = getRFCfunction(destination, 494 | SAPConstants.BAPI_TRANSACTION_ROLLBACK); 495 | evaluateRFCfunction(rollbackFunction, destination, escapeErrorHandling); 496 | log.warn("Rolled-back transaction. Function: " + rfcFunctionName); 497 | } 498 | sendFault(messageContext, e, SAP_TRANSPORT_ERROR); 499 | } finally { 500 | if (isTransaction(messageContext) || isLogon) { 501 | //end transaction 502 | JCoContext.end(destination); 503 | if (log.isDebugEnabled()) { 504 | log.debug("Ended transaction. Function: " + rfcFunctionName); 505 | } 506 | } 507 | } 508 | } 509 | 510 | private void populateBapiReponse(MessageContext messageContext, SAPOutTransportInfo outTransportInfo) { 511 | 512 | synchronized (outTransportInfo) { 513 | log.debug("Populating response to outTransportInfo"); 514 | outTransportInfo.setPayload(messageContext.getEnvelope().getBody()); 515 | outTransportInfo.notify(); 516 | } 517 | } 518 | } 519 | --------------------------------------------------------------------------------