SAP通過JCO調用Java程序配置開發

最近接到一任務,需求是SAP將採購請求發送給第三方EDI程序,我的任務是將第三方EDI接口封裝好,然後供本公司SAP程序調用。之前都是JAVA通過JCO來調用SAP的RFC程序,這已經很熟悉了,沒有任何問題。現在反過來調用,還是頭一回,查閱了大量資料,終於搞定。現將研究成果與大家分享,避免大家走許多彎路。項目框架採用的是springmvc,spring初始化的時候加載該服務。

準備工作

安裝JCo3
JCo有32位和64爲之分,32位的JVM選擇32位的JCO, 64位的JVM選擇64位的JCO, 在windows環境,選擇相應的sapjco3.dll, Unix和Linux環境選擇合適的sapjco3.so,這裏不多做介紹。如果服務器是window操作系統,見博客no sapjco3 in java.library.path,如果服務器是linux操作系統,具體安裝方法見博客linux配置sapjco3

ABAP訪問Java服務

1、ABAP端

ABAP(作爲Clint端),調用JAVA(作爲服務器端)。
SAP通過JCO反向調用JAVA的RFC服務其實也是相對簡單的,只是在JAVA端需要使用JCO創建一個RFC服務,然後在SAP端註冊這個服務程序。
首先,JCo服務器程序需在網關中進行註冊,在SM59中,定義一個連接類型爲T的遠程目標。



RFC目標系統:是ABAP RFC調用Java時,需指定的目標系統名。
Program ID:是JAVA程序中使用的。

Gateway Host與Gateway service值來自以下界面(Tcode:SMGW):



所有配置好且Java服務器代碼跑起來後,點擊“Connection Test”按鈕,如不出現紅色文本,則表示鏈接成功(注:此時需要ServerDataProvider.JCO_PROGID設置的Program ID要與SM59中設置的相同,否則測試不成功。另要注意的是:即使Java服務器設置的Program ID亂設置,Java服務端還是能啓起來,但ABAP測試連接時會不成功,也就代表ABAP不能調用Java):



此時可以通過SMGW來觀測連接:


如果出現如下異常:

連接異常registrationnot allowed
Java服務啓動時,如出現以下異常,則需在SAP中修改網關參數:
com.sap.conn.jco.JCoException: (113) JCO_ERROR_REGISTRATION_DENIED: CPIC-CALL: SAP_CMACCPTP3 on convId:       
LOCATION    SAP-Gateway on host LRP-ERP / sapgw00
ERROR       registration of tp JCOTEST from host JIANGZHENGJUN not allowed
TIME        Wed Apr 16 21:25:39 2014
RELEASE     720
COMPONENT   SAP-Gateway
VERSION     2
RC          720
MODULE      gwxxrd.c
LINE        3612
COUNTER     275



代碼如下:

*&---------------------------------------------------------------------*
*& Report  ZRMMLQ_REQUEST_OUT
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT  zrmmlq_request_out.

TABLES: zmmlqpo_d,
        zmmlqpo_h.

DATA: requesth TYPE zslq_request_out OCCURS 0 WITH HEADER LINE,
      requestd TYPE zslq_request_out_item OCCURS 0 WITH HEADER LINE.

DATA: i_h LIKE zmmlqpo_h OCCURS 0 WITH HEADER LINE,
      i_d LIKE zmmlqpo_d OCCURS 0 WITH HEADER LINE.

DATA: message TYPE char100.
DATA: v_p(6) TYPE c.
DATA: v_q(6) TYPE c.
DATA: v_string TYPE string.

SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001.
SELECT-OPTIONS: s_ebeln FOR zmmlqpo_h-ebeln,
                s_aedat FOR zmmlqpo_h-aedat.

SELECTION-SCREEN END OF BLOCK b1.

START-OF-SELECTION.


  SELECT * FROM zmmlqpo_h INTO TABLE i_h
    WHERE ebeln IN s_ebeln
      AND aedat IN s_aedat
      AND status = '02'.
  IF sy-subrc EQ 0.
    SELECT * FROM zmmlqpo_d INTO TABLE i_d
      FOR ALL ENTRIES IN i_h
      WHERE ebeln = i_h-ebeln.

    LOOP AT i_h.
      MOVE i_h-aedat TO requesth-porchasedate.
      MOVE i_h-ebeln TO requesth-pono.
      MOVE i_h-thirdparty TO requesth-thirdparty.



*      request-others
      requesth-signtype = 'B'.
*      request-purchasecontact
*      request-contactareatel
*      request-contacttel

**      CLEAR orderitem.
**      CLEAR orderitem[].
*
*
**      request-orderitem = orderitem[].

      SELECT SINGLE province
city
district
county
street
segment
tunnel
znum
        FROM zmd_sqxx1
        INTO (requesth-province,
      requesth-city,
      requesth-area,
      requesth-village,
      requesth-road,
      requesth-section,
      requesth-lane,
      requesth-oddeven
        )
        WHERE zzmd_mdbm = requesth-shiptocust.

      READ TABLE i_d WITH KEY ebeln = i_h-ebeln.
      IF sy-subrc EQ 0.
        MOVE i_d-werks TO requesth-shiptocust.
      ENDIF.

      APPEND requesth.
      CLEAR requesth.
    ENDLOOP.

    LOOP AT i_d  .
      MOVE i_d-ebeln TO requestd-pono.


*        requestd-POCOMMENTS
      MOVE i_d-ebelp TO requestd-polineno.
      MOVE i_d-matnr TO requestd-stockcode.
*requestd-UNITPRICE
      MOVE i_d-menge TO v_string.
      SPLIT v_string AT '.' INTO v_p v_q.
      MOVE v_p TO requestd-orderqty.
*requestd-DISCOUNT
*requestd-PROMOTION
      APPEND requestd.
    ENDLOOP.

    CALL FUNCTION 'ZFM_LQ_REQUEST_OUT' DESTINATION 'ZLQTEST'
      IMPORTING
        message  = message
      TABLES
        requesth = requesth
        requestd = requestd.

    IF message = 'SUCCESS'.
      LOOP AT i_h.
        i_h-status = '01'.
        MODIFY i_h.
      ENDLOOP.
    ELSE.
      LOOP AT i_h.
        i_h-status = '00'.
        MODIFY i_h.
      ENDLOOP.
    ENDIF.
    MODIFY zmmlqpo_h FROM TABLE i_h.
*    CALL FUNCTION 'ZFM_LQ_REQUEST_OUT' DESTINATION 'ZLQTEST'
*      TABLES
*        requesth = requesth
*        requestd = requestd.


  ENDIF.

對應的RFC結構如下:


程度運行結果如下:


2、JAVA端

採購單頭代碼如下:
package com.pcmall.domain.vo;

import java.util.Date;
import java.util.List;

public class SapPo {
	private String PorchaseDate;
	private String PoNo;
	private String ThirdParty;
	private String ShipToCust;
	private String Province;
	private String City;
	private String Area;
	private String Village;
	private String Road;
	private String Section;
	private String Lane;
	private String Oddeven;
	private String Others;
	private String SignType;
	private String PurchaseContact;
	private String ContactAreaTel;
	private String ContactTel;
	private List<SapPod> pods;
	
	public String getPorchaseDate() {
		return PorchaseDate;
	}
	public void setPorchaseDate(String porchaseDate) {
		PorchaseDate = porchaseDate;
	}
	public String getPoNo() {
		return PoNo;
	}
	public void setPoNo(String poNo) {
		PoNo = poNo;
	}
	public String getThirdParty() {
		return ThirdParty;
	}
	public void setThirdParty(String thirdParty) {
		ThirdParty = thirdParty;
	}
	public String getShipToCust() {
		return ShipToCust;
	}
	public void setShipToCust(String shipToCust) {
		ShipToCust = shipToCust;
	}
	public String getProvince() {
		return Province;
	}
	public void setProvince(String province) {
		Province = province;
	}
	public String getCity() {
		return City;
	}
	public void setCity(String city) {
		City = city;
	}
	public String getArea() {
		return Area;
	}
	public void setArea(String area) {
		Area = area;
	}
	public String getVillage() {
		return Village;
	}
	public void setVillage(String village) {
		Village = village;
	}
	public String getRoad() {
		return Road;
	}
	public void setRoad(String road) {
		Road = road;
	}
	public String getSection() {
		return Section;
	}
	public void setSection(String section) {
		Section = section;
	}
	public String getLane() {
		return Lane;
	}
	public void setLane(String lane) {
		Lane = lane;
	}
	public String getOddeven() {
		return Oddeven;
	}
	public void setOddeven(String oddeven) {
		Oddeven = oddeven;
	}
	public String getOthers() {
		return Others;
	}
	public void setOthers(String others) {
		Others = others;
	}
	public String getSignType() {
		return SignType;
	}
	public void setSignType(String signType) {
		SignType = signType;
	}
	public String getPurchaseContact() {
		return PurchaseContact;
	}
	public void setPurchaseContact(String purchaseContact) {
		PurchaseContact = purchaseContact;
	}
	public String getContactAreaTel() {
		return ContactAreaTel;
	}
	public void setContactAreaTel(String contactAreaTel) {
		ContactAreaTel = contactAreaTel;
	}
	public String getContactTel() {
		return ContactTel;
	}
	public void setContactTel(String contactTel) {
		ContactTel = contactTel;
	}
	public List<SapPod> getPods() {
		return pods;
	}
	public void setPods(List<SapPod> pods) {
		this.pods = pods;
	}
}
採購單行代碼如下:
package com.pcmall.domain.vo;

public class SapPod {
	private String PoComments;
	private String PoLineNo;
	private String StockCode;
	private String UnitPrice;
	private String OrderQty;
	private String Discount;
	private String Promotion;
	
	public String getPoComments() {
		return PoComments;
	}
	public void setPoComments(String poComments) {
		PoComments = poComments;
	}
	public String getPoLineNo() {
		return PoLineNo;
	}
	public void setPoLineNo(String poLineNo) {
		PoLineNo = poLineNo;
	}
	public String getStockCode() {
		return StockCode;
	}
	public void setStockCode(String stockCode) {
		StockCode = stockCode;
	}
	public String getUnitPrice() {
		return UnitPrice;
	}
	public void setUnitPrice(String unitPrice) {
		UnitPrice = unitPrice;
	}
	public String getOrderQty() {
		return OrderQty;
	}
	public void setOrderQty(String orderQty) {
		OrderQty = orderQty;
	}
	public String getDiscount() {
		return Discount;
	}
	public void setDiscount(String discount) {
		Discount = discount;
	}
	public String getPromotion() {
		return Promotion;
	}
	public void setPromotion(String promotion) {
		Promotion = promotion;
	}
}
SAPJCO連接類代碼如下:
package com.pcmall.common.sap;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import com.sap.conn.jco.JCo;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.ServerDataProvider;

public class SapjcoConnector {
	private static Logger logger = LoggerFactory
			.getLogger(SapjcoConnector.class);

	static Hashtable<String, JCoDestination> destinations = new Hashtable<String, JCoDestination>();

	public static String SERVER_NAME = "SANPOWER_SERVER";
	public static String DESTINATION_NAME = "ABAP_AS_WITH_POOL";

	static {
		try {
			doInitialize();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 初始化SAP連接
	 * 
	 * @param fdPoolName
	 * @return
	 * @throws Exception
	 */
	public static synchronized void doInitialize() throws Exception {
		JCo.setTrace(4, null);// 打開調試
		Properties connectProperties = new Properties();
		
		InputStream is = SapjcoConnector.class
				.getResourceAsStream("/properties/sap-config.properties");
		connectProperties.load(is);
		createDataFile(DESTINATION_NAME, "jcoDestination", connectProperties);
		
		
		
		
		// ******JCo sever
		Properties servertProperties = new Properties();
		InputStream isServer = SapjcoConnector.class
				.getResourceAsStream("/properties/sap-config-server.properties");
		servertProperties.load(isServer);
		
		
		/*servertProperties.setProperty(ServerDataProvider.JCO_GWHOST,
				"xxx.xxx.xxx.xxx");
		// TCP服務sapgw是固定的,後面的00就是SAP實例系統編號,也可直接是端口號(端口號可以在
		// etc/server文件中找sapgw00所對應的端口號)
		servertProperties.setProperty(ServerDataProvider.JCO_GWSERV, "sapgw30");
		// 這裏的程序ID來自於SM59中設置的Program ID,必須相同
		servertProperties
				.setProperty(ServerDataProvider.JCO_PROGID, "JCO_SANPOWER_LQ");
		servertProperties.setProperty(ServerDataProvider.JCO_REP_DEST,
				DESTINATION_NAME);
		servertProperties.setProperty(ServerDataProvider.JCO_CONNECTION_COUNT,
				"2");*/
		
		
		
		createDataFile(SERVER_NAME, "jcoServer", servertProperties);
	}

	/**
	 * 創建連接文件(必須)
	 * 
	 * @param name
	 * @param suffix
	 * @param properties
	 */
	private static void createDataFile(String name, String suffix,
			Properties properties) {
		File cfg = new File(name + "." + suffix);
		if (cfg.exists()) {
			cfg.deleteOnExit();
		}
		try {
			FileOutputStream fos = new FileOutputStream(cfg, false);
			properties.store(fos, "for connection");
			fos.close();
			logger.info("createDataFile: " + name + "." + suffix);
		} catch (Exception e) {
			throw new RuntimeException(
					"Unable to create the destination file "
							+ cfg.getName(), e);
		}
	}

	/**
	 * 獲取連接池,利用JCoDestinationManager創建連接池放到表中
	 * 
	 * @param fdPoolName
	 * @return
	 * @throws Exception
	 */
	public static JCoDestination getDestination(String fdPoolName)
			throws Exception {
		JCoDestination destination = null;
		if (destinations.containsKey(fdPoolName)) {
			destination = destinations.get(fdPoolName);
		} else {
			destination = JCoDestinationManager.getDestination(fdPoolName);
			destinations.put(fdPoolName, destination);
		}
		return destination;
	}

}


Service層代碼如下:
package com.pcmall.service.sap.impl;

import java.io.ByteArrayOutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.stereotype.Service;

import com.pcmall.common.sap.SapConnector;
import com.pcmall.common.sap.SapjcoConnector;
import com.pcmall.common.utils.XmlConverUtil;
import com.pcmall.domain.vo.ResponseVO;
import com.pcmall.domain.vo.SapPo;
import com.pcmall.domain.vo.SapPod;
import com.pcmall.domain.vo.SapPurchaseAttachDetailVO;
import com.pcmall.domain.vo.SapPurchaseAttachVO;
import com.pcmall.service.sap.ISapService;
import com.pcmall.service.sap.impl.SapRfc.StfcConnectionHandler;
import com.sap.conn.jco.JCo;
import com.sap.conn.jco.JCoCustomRepository;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.JCoFunction;
import com.sap.conn.jco.JCoFunctionTemplate;
import com.sap.conn.jco.JCoListMetaData;
import com.sap.conn.jco.JCoMetaData;
import com.sap.conn.jco.JCoParameterList;
import com.sap.conn.jco.JCoRecord;
import com.sap.conn.jco.JCoRepository;
import com.sap.conn.jco.JCoTable;
import com.sap.conn.jco.server.DefaultServerHandlerFactory;
import com.sap.conn.jco.server.JCoServer;
import com.sap.conn.jco.server.JCoServerContext;
import com.sap.conn.jco.server.JCoServerContextInfo;
import com.sap.conn.jco.server.JCoServerErrorListener;
import com.sap.conn.jco.server.JCoServerExceptionListener;
import com.sap.conn.jco.server.JCoServerFactory;
import com.sap.conn.jco.server.JCoServerFunctionHandler;
import com.sap.conn.jco.server.JCoServerState;
import com.sap.conn.jco.server.JCoServerStateChangedListener;

@Service
public class SapServiceImpl implements ISapService {

	@Override
	public ResponseVO submitPurchaseAttach(SapPurchaseAttachVO attach)
			throws Exception {

		ResponseVO responseVO = new ResponseVO();
		String sParam[] = null;
		JCoDestination foo = SapConnector
				.getDestination(SapConnector.fdPoolName);
		JCoRepository mRepository = foo.getRepository();
		JCoFunctionTemplate ft = mRepository.getFunctionTemplate("ZFMHK_ORDER");
		JCoFunction function = ft.getFunction();
		JCoParameterList input = function.getImportParameterList();
		// 設置輸入參數
		// 設置輸入參數
		JCoTable tableDataH = function.getTableParameterList().getTable(
				"I_ZTHK0001H");

		tableDataH.appendRow();
		tableDataH.setValue("MANDT", attach.getMANDT());
		tableDataH.setValue("ZERPNO", attach.getZERPNO());
		tableDataH.setValue("ZTYPE", attach.getZTYPE());
		tableDataH.setValue("BUKRS_F", attach.getBUKRS_F());
		tableDataH.setValue("KOSTL_F", attach.getKOSTL_F());
		tableDataH.setValue("PRCTR_F", attach.getPRCTR_F());
		tableDataH.setValue("PRCTR_F", attach.getPRCTR_F());
		tableDataH.setValue("ZFROMNO", attach.getZFROMNO());
		tableDataH.setValue("BUKRS_T", attach.getBUKRS_T());
		tableDataH.setValue("KOSTL_T", attach.getKOSTL_T());
		tableDataH.setValue("PRCTR_T", attach.getPRCTR_T());
		tableDataH.setValue("ZTONO", attach.getZTONO());
		tableDataH.setValue("ZSTATUS", attach.getZSTATUS());
		tableDataH.setValue("EKORG", attach.getEKORG());
		tableDataH.setValue("LIFNR", attach.getLIFNR());
		tableDataH.setValue("LIFNR_TXT", attach.getLIFNR_TXT());
		tableDataH.setValue("ZBEIZHU", attach.getZBEIZHU());
		tableDataH.setValue("BUDAT", attach.getBUDAT());
		tableDataH.setValue("ZOUTIN", attach.getZOUTIN());
		tableDataH.setValue("BELNR1", attach.getBELNR1());
		tableDataH.setValue("BELNR2", attach.getBELNR2());
		tableDataH.setValue("ERDAT", attach.getERDAT());
		tableDataH.setValue("ZCRE_DATE", attach.getZCRE_DATE());
		tableDataH.setValue("ZCRE_TIME", attach.getZCRE_TIME());
		tableDataH.setValue("ZCRE_USER", attach.getZCRE_USER());
		tableDataH.setValue("ZUP_DATE", attach.getZUP_DATE());
		tableDataH.setValue("ZUP_TIME", attach.getZUP_TIME());
		tableDataH.setValue("ZUP_USER", attach.getZUP_USER());
		tableDataH.setValue("NETWR2", attach.getNETWR2());

		JCoTable tableData = function.getTableParameterList().getTable(
				"I_ZTHK0001D");
		for (SapPurchaseAttachDetailVO detail : attach.getDetails()) {
			tableData.appendRow();
			tableData.setValue("MANDT", detail.getMANDT());
			tableData.setValue("ZERPNO", detail.getZERPNO());
			tableData.setValue("POSNUM", detail.getPOSNUM());
			tableData.setValue("MATNR", detail.getMATNR());
			tableData.setValue("MAKTX", detail.getMAKTX());
			tableData.setValue("MENGE", detail.getMENGE());
			tableData.setValue("NETPR", detail.getNETPR());
			tableData.setValue("NETWR", detail.getNETWR());
			// tableData.setValue("NETWR2",detail.getNETWR2());
			tableData.setValue("MWSBP", detail.getMWSBP());
			tableData.setValue("ZOUTIN", detail.getZOUTIN());
		}

		// 執行function
		function.execute(foo);

		// 得到輸出參數TABLE
		// JCoTable tableExport =
		// function.getTableParameterList().getTable("RETURN");

		String result = function.getExportParameterList().getString("RETCODE");
		String message = function.getExportParameterList().getString("RETMSG");
		if (result.equals("E")) {
			responseVO.setSuccess(false);
			responseVO.setErrorMsg(message);

		} else {
			responseVO.setSuccess(true);
		}

		return responseVO;
	}

	@Override
	public ResponseVO submitPo2Lq() {
		// TODO Auto-generated method stub
		return null;
	}

	// 在Java服務端定義遠程函數(不需要在ABAP端進行函數的簽名定義)
	@PostConstruct
	static void startPo2LqRfc() {
		/*
		 * JCoListMetaData impList = JCo.createListMetaData("IMPORT");
		 * impList.add("REQUTEXT", JCoMetaData.TYPE_CHAR, 100, 50, 0, null,
		 * null, JCoListMetaData.IMPORT_PARAMETER, null, null);
		 * impList.lock();// 鎖住,不允許再修改
		 */
		JCoListMetaData expList = JCo.createListMetaData("EXPORT");
		expList.add("MESSAGE", JCoMetaData.TYPE_CHAR, 100, 50, 0, null, null,
				JCoListMetaData.EXPORT_PARAMETER, null, null);
		expList.lock();

		JCoListMetaData tblList = JCo.createListMetaData("TABLE");
		tblList.add("REQUESTH", JCoMetaData.TYPE_TABLE, 1000, 500, 0, null, null,
				JCoListMetaData.OPTIONAL_PARAMETER, "ZSLQ_REQUEST_OUT", null);
		tblList.add("REQUESTD", JCoMetaData.TYPE_TABLE, 1000, 500, 0, null, null,
				JCoListMetaData.OPTIONAL_PARAMETER, "ZSLQ_REQUEST_OUT_ITEM", null);
		tblList.lock();
		
		

		// 注:ZSTFC_CONNECTION函數不必要在ABAP端時行定義了(只定義簽名,不需要實現),因爲在這裏(Java)
		// 進行了動態的函數對象創建的創建與註冊,這與上面simpleServer方法示例是不一樣的
		JCoFunctionTemplate fT = JCo.createFunctionTemplate("ZFM_LQ_REQUEST_OUT",
				null, expList, null, tblList, null);
		JCoCustomRepository cR = JCo
				.createCustomRepository("MyCustomRepository");
		cR.addFunctionTemplateToCache(fT);
		JCoServer server;
		try {
			server = JCoServerFactory.getServer(SapjcoConnector.SERVER_NAME);
		} catch (JCoException ex) {
			throw new RuntimeException("Unable to create the server "
					+ SapjcoConnector.SERVER_NAME + " because of "
					+ ex.getMessage(), ex);
		}
		String repDest = server.getRepositoryDestination();
		if (repDest != null) {
			try {
				cR.setDestination(JCoDestinationManager.getDestination(repDest));
			} catch (JCoException e) {
				e.printStackTrace();
				System.out
						.println(">>> repository contains static function definition only");
			}
		}
		server.setRepository(cR);
		JCoServerFunctionHandler requestHandler = new StfcConnectionHandler();
		DefaultServerHandlerFactory.FunctionHandlerFactory factory = new DefaultServerHandlerFactory.FunctionHandlerFactory();
		factory.registerHandler(fT.getName(), requestHandler);
		server.setCallHandlerFactory(factory);
		server.start();
	}

	// 處理來自ABAP端的調用請求,實現註冊過的虛擬函數真正功能
	static class StfcConnectionHandler implements JCoServerFunctionHandler {
		public void handleRequest(JCoServerContext serverCtx,
				JCoFunction function) {// 處理遠程調用請求
			printServerInfo(serverCtx, function);

			JCoTable poTable = function.getTableParameterList().getTable(
					"REQUESTH");
			JCoTable podTable = function.getTableParameterList().getTable(
					"REQUESTD");
			
			List<SapPo> listPo = new ArrayList<SapPo>();
			
			for(int i=0;i<poTable.getNumRows();i++){
				poTable.setRow(i);
				
				SapPo po = new SapPo();
				SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
				Date date = null;
				try {
					date = formatter.parse(poTable.getString("PORCHASEDATE"));
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				String dateString = formatter.format(date);
				
				
				po.setPorchaseDate(dateString);
				po.setPoNo(poTable.getString("PONO"));
				po.setThirdParty(poTable.getString("THIRDPARTY"));
				po.setShipToCust(poTable.getString("SHIPTOCUST"));
				po.setProvince(poTable.getString("PROVINCE"));
				po.setCity(poTable.getString("CITY"));
				po.setArea(poTable.getString("AREA"));
				po.setVillage(poTable.getString("VILLAGE"));
				po.setRoad(poTable.getString("ROAD"));
				po.setSection(poTable.getString("SECTION"));
				po.setLane(poTable.getString("LANE"));
				po.setOddeven(poTable.getString("ODDEVEN"));
				po.setOthers(poTable.getString("OTHERS"));
				po.setSignType(poTable.getString("SIGNTYPE"));
				po.setPurchaseContact(poTable.getString("PURCHASECONTACT"));
				po.setContactAreaTel(poTable.getString("CONTACTAREATEL"));
				po.setContactTel(poTable.getString("CONTACTTEL"));
				
				List<SapPod> listPod = new ArrayList<SapPod>();
				for(int j=0;j<podTable.getNumRows();j++){
					podTable.setRow(j);
					if(podTable.getString("PONO").equals(poTable.getString("PONO"))){
						SapPod pod = new SapPod();
						
						pod.setPoComments(podTable.getString("POCOMMENTS"));
						pod.setPoLineNo(podTable.getString("POLINENO"));
						pod.setStockCode(podTable.getString("STOCKCODE"));
						pod.setUnitPrice(podTable.getString("UNITPRICE"));
						pod.setOrderQty(podTable.getString("ORDERQTY"));
						pod.setDiscount(podTable.getString("DISCOUNT"));
						pod.setPromotion(podTable.getString("PROMOTION"));
						
						listPod.add(pod);
					}
				}
				po.setPods(listPod);
				listPo.add(po);
			}
			
			try {
				System.out.println(listtoXml(listPo));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			function.getExportParameterList().setValue("MESSAGE", "SUCCESS");
		}

		public void printServerInfo(JCoServerContext serverCtx,
				JCoFunction function) {
			System.out
					.println("----------------------------------------------------------------");
			System.out.println("call              : " + function.getName());// ABAP調用的是哪個函數
			System.out.println("ConnectionId      : "
					+ serverCtx.getConnectionID());
			System.out.println("SessionId         : "
					+ serverCtx.getSessionID());
			System.out.println("TID               : " + serverCtx.getTID());
			System.out.println("repository name   : "
					+ serverCtx.getRepository().getName());
			System.out.println("is in transaction : "
					+ serverCtx.isInTransaction());
			System.out.println("is stateful       : "
					+ serverCtx.isStatefulSession());
			System.out
					.println("----------------------------------------------------------------");
			System.out.println("gwhost: "
					+ serverCtx.getServer().getGatewayHost());
			System.out.println("gwserv: "
					+ serverCtx.getServer().getGatewayService());
			System.out.println("progid: "
					+ serverCtx.getServer().getProgramID());
			System.out
					.println("----------------------------------------------------------------");
			System.out.println("attributes  : ");
			System.out.println(serverCtx.getConnectionAttributes().toString());
			System.out
					.println("----------------------------------------------------------------");
			System.out.println("CPIC conversation ID: "
					+ serverCtx.getConnectionAttributes()
							.getCPICConversationID());
			System.out
					.println("----------------------------------------------------------------");
		}
	}

	static class MyThrowableListener implements JCoServerErrorListener,
			JCoServerExceptionListener {// 服務異常監聽器
		public void serverErrorOccurred(JCoServer jcoServer,
				String connectionId, JCoServerContextInfo serverCtx, Error error) {
			System.out.println(">>> Error occured on "
					+ jcoServer.getProgramID() + " connection " + connectionId);
			error.printStackTrace();
		}

		public void serverExceptionOccurred(JCoServer jcoServer,
				String connectionId, JCoServerContextInfo serverCtx,
				Exception error) {
			System.out.println(">>> Error occured on "
					+ jcoServer.getProgramID() + " connection " + connectionId);
			error.printStackTrace();
		}
	}

	static class MyStateChangedListener implements
			JCoServerStateChangedListener {// 服務狀態改變監聽器
		public void serverStateChangeOccurred(JCoServer server,
				JCoServerState oldState, JCoServerState newState) {
			// Defined states are: STARTED啓動, DEAD死, ALIVE活, STOPPED停止;
			// see JCoServerState class for details.
			// Details for connections managed by a server instance
			// are available via JCoServerMonitor.
			System.out.println("Server state changed from "
					+ oldState.toString() + " to " + newState.toString()
					+ " on server with program id " + server.getProgramID());
		}
	}
	
	/**
	 * 
	 * @param document
	 * @return
	 */
	public static String doc2String(Document document) {
		String s = "";
		try {
			// 使用輸出流來進行轉化
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			// 使用UTF-8編碼
			OutputFormat format = new OutputFormat("   ", true, "UTF-8");
			XMLWriter writer = new XMLWriter(out, format);
			writer.write(document);
			s = out.toString("UTF-8");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return s;
	}
	
	public static String listtoXml(List<SapPo> listSapPo) throws Exception{
		//創建文檔及設置根元素節點的方式  
        Element root = DocumentHelper.createElement("PurchaseOrdersRequest");  
        Document document = DocumentHelper.createDocument(root);  
        
        Element baseID = root.addElement("BaseID").addText("10321");
        Element purchaseOrderList = root.addElement("PurchaseOrderList");
        
        for(SapPo po : listSapPo){
        	Element purchaseOrder = purchaseOrderList.addElement("PurchaseOrder");
        	Element porchaseDate = purchaseOrder.addElement("PorchaseDate").addText(po.getPorchaseDate());
        	Element poNo = purchaseOrder.addElement("PoNo").addText(po.getPoNo());
        	Element thirdParty = purchaseOrder.addElement("ThirdParty").addText(po.getThirdParty());
        	Element shipToCust = purchaseOrder.addElement("shipToCust").addText(po.getShipToCust());
        	Element province = purchaseOrder.addElement("Province").addText(po.getProvince());
        	Element city = purchaseOrder.addElement("City").addText(po.getCity());
        	Element area = purchaseOrder.addElement("Area").addText(po.getArea());
        	Element village = purchaseOrder.addElement("Village").addText(po.getVillage());
        	Element road = purchaseOrder.addElement("Road").addText(po.getRoad());
        	Element section = purchaseOrder.addElement("Section").addText(po.getSection());
        	Element lane = purchaseOrder.addElement("Lane").addText(po.getLane());
        	Element oddeven = purchaseOrder.addElement("Oddeven").addText(po.getOddeven());
        	Element others = purchaseOrder.addElement("Others").addText(po.getOthers());
        	Element signType = purchaseOrder.addElement("SignType").addText(po.getSignType());
        	Element purchaseContact = purchaseOrder.addElement("PurchaseContact").addText(po.getPurchaseContact());
        	Element contactAreaTel = purchaseOrder.addElement("contactAreaTel").addText(po.getContactAreaTel());
        	Element contactTel = purchaseOrder.addElement("contactTel").addText(po.getContactTel());
        	
        	Element orderItemList = purchaseOrder.addElement("OrderItemList");
        	for(SapPod pod : po.getPods()){
        		Element orderItem = orderItemList.addElement("OrderItem");
        		Element poComments = orderItem.addElement("PoComments").addText(pod.getPoComments());
        		Element poLineNo = orderItem.addElement("PoLineNo").addText(pod.getPoLineNo());
        		Element stockCode = orderItem.addElement("StockCode").addText(pod.getStockCode());
        		Element unitPrice = orderItem.addElement("UnitPrice").addText(pod.getUnitPrice());
        		Element orderQty = orderItem.addElement("OrderQty").addText(pod.getOrderQty());
        		Element discount = orderItem.addElement("Discount").addText(pod.getDiscount());
        		Element promotion = orderItem.addElement("Promotion").addText(pod.getPromotion());
        	}
        }
        
        String strXml = doc2String(document);
        return strXml;
	}

}

3、ABAP端調用

ABAP端調用後,JAVA根據採購單列表進行拼接XML,生成的XML文件如下,供第三方EDI做爲入參。
<?xml version="1.0" encoding="utf-8"?>

<PurchaseOrdersRequest> 
  <BaseID>10321</BaseID>  
  <PurchaseOrderList> 
    <PurchaseOrder> 
      <PorchaseDate>2017-04-17</PorchaseDate>  
      <PoNo>4500004860</PoNo>  
      <ThirdParty>N</ThirdParty>  
      <shipToCust/>  
      <Province/>  
      <City/>  
      <Area/>  
      <Village/>  
      <Road/>  
      <Section/>  
      <Lane/>  
      <Oddeven/>  
      <Others/>  
      <SignType>B</SignType>  
      <PurchaseContact/>  
      <contactAreaTel/>  
      <contactTel/>  
      <OrderItemList/> 
    </PurchaseOrder>  
    <PurchaseOrder> 
      <PorchaseDate>2017-05-03</PorchaseDate>  
      <PoNo>4500004863</PoNo>  
      <ThirdParty>N</ThirdParty>  
      <shipToCust>D000</shipToCust>  
      <Province/>  
      <City/>  
      <Area/>  
      <Village/>  
      <Road/>  
      <Section/>  
      <Lane/>  
      <Oddeven/>  
      <Others/>  
      <SignType>B</SignType>  
      <PurchaseContact/>  
      <contactAreaTel/>  
      <contactTel/>  
      <OrderItemList> 
        <OrderItem> 
          <PoComments/>  
          <PoLineNo>00001</PoLineNo>  
          <StockCode>000000000000106261</StockCode>  
          <UnitPrice/>  
          <OrderQty>10</OrderQty>  
          <Discount/>  
          <Promotion/> 
        </OrderItem> 
      </OrderItemList> 
    </PurchaseOrder>  
    <PurchaseOrder> 
      <PorchaseDate>2017-05-03</PorchaseDate>  
      <PoNo>4500004864</PoNo>  
      <ThirdParty>N</ThirdParty>  
      <shipToCust>D000</shipToCust>  
      <Province/>  
      <City/>  
      <Area/>  
      <Village/>  
      <Road/>  
      <Section/>  
      <Lane/>  
      <Oddeven/>  
      <Others/>  
      <SignType>B</SignType>  
      <PurchaseContact/>  
      <contactAreaTel/>  
      <contactTel/>  
      <OrderItemList> 
        <OrderItem> 
          <PoComments/>  
          <PoLineNo>00001</PoLineNo>  
          <StockCode>000000000000106261</StockCode>  
          <UnitPrice/>  
          <OrderQty>10</OrderQty>  
          <Discount/>  
          <Promotion/> 
        </OrderItem>  
        <OrderItem> 
          <PoComments/>  
          <PoLineNo>00002</PoLineNo>  
          <StockCode>000000000000106262</StockCode>  
          <UnitPrice/>  
          <OrderQty>2</OrderQty>  
          <Discount/>  
          <Promotion/> 
        </OrderItem> 
      </OrderItemList> 
    </PurchaseOrder> 
  </PurchaseOrderList> 
</PurchaseOrdersRequest>

4、常見問題

開發完畢之後,我就將程序發佈到Linux測試服務器上,結果SAP端通過SMGW是可以連接的,但是通過SM59測試連接,死活連接不通。一直報如下錯誤:

後來想是不是Linux服務器問題啊,我們領導就讓我在Windows服務器上再部署試一下,結果還是不行,不過它報錯了,一直報"sapgw30 service unknow...",後來一想我機器上是安裝了SAP客戶端,目錄“C:\Windows\System32\drivers\etc"下的services文件中有sapgw30服務,這樣我們在services文件中加入sapgw30服務,就可以了。
同理,我們在Linux服務器上/etc/services也添加sapgw30服務,
sapgw30  3330/tcp
這下就能成功連接了。

注:
/etc/services文件包含了服務名和端口號之間的映射,很多的系統程序要使用這個文件。一般情況下,不要修改該文件的內容,因爲這些設置都是Internet標準的設置。一旦修改,可能會造成系統衝突,使用戶無法正常訪問資源。Linux系統的端口號的範圍爲0–65535,不同範圍有不同的意義。
0 不使用
1--1023 系統保留,只能由root用戶使用
1024---4999 由客戶端程序自由分配
5000---65535 由服務器端程序自由分配

至此大功告成!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章