SAP接口編程 之 JCo3.0 系列 (01):JCoDestination

JCo3.0 是 Java 語言與 ABAP 語言雙向通信的中間件。與之前 1.0/2.0 相比,是重新設計的產品,調用 API 和架構設計與 NCo3.0 比較類似。實際上,NCo3.0 的設計參考了 JCo3.0。從本篇開始,系統介紹 JCo3.0 代碼編寫的技術要點。

JCo3.0 安裝

https://service.sap.com/connectors 可以下載 JCo3.0,注意下載的版本要與 操作系統JVM 版本(32 位還是 64位)匹配。將文件解壓到目標文件夾。以 Windows 系統為例,主要的文件包括:

  • sapjco3.dll
  • sapjco3.jar

SAP 強烈推薦將這兩個文件放在同一文件夾下。測試安裝是否成功,可以在命令窗口下,進入安裝文件夾,運行下面的命令:

java -jar sapjco3.jar

如果安裝成功,應該顯示如下界面:

jco3安裝成功的顯示界面

JCoDestination 對象

JCoDestination 代表后端 SAP 系統,與之前版本顯式連接到 SAP 系統不同,JCo3.0 運行時環境負責管理連接,包括建立連接和釋放連接,開發者不需要關心與 SAP 的連接。我們先通過一個簡單的例子,了解 JCo3.0 JCoDestination 類的一些要點。以下講述基于 IDEA Community 版本,如果編程環境是 Eclipse,也大同小異。

  • 新建一個 Java 項目,項目名為 JCo3Demo。
  • 在項目文件夾下新建一個 packages 文件夾,將 sapjco3.jar 和 sapjco3.dll 拷貝到該文件夾下。選中該文件夾,右鍵,選擇菜單項 Add as Library,作用是將 jar 包加入到 Build Path 中。
  • 在項目的根文件夾下,新建一個文本文件,文件名命名為 ECC.jocdestination,在這個文件中設置與 SAP 系統連接的的相關參數。文件的內容如下:
#SAP Logon parameters!
#Tue Dec 08 16:41:30 CST 2015
jco.client.lang=EN
jco.client.client=001
jco.client.passwd=xxxxxx
jco.client.user=STONE
jco.client.sysnr=00
jco.client.ashost=192.168.65.100

對照SAP GUI,不難理解各個參數的作用:

SAP GUI

環境準備好了,先來一段最簡單的代碼,測試是否可以連接到 SAP 系統:

package jco3.demo1;

import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import org.junit.Test;

public class JCoDestinationDemo {
    public JCoDestination getDestination() throws JCoException {
        /**
         * Get instance of JCoDestination from file: ECC.jcodestination
         * which should be located in the root folder of project
         */

        JCoDestination dest = JCoDestinationManager.getDestination("ECC");
        return dest;
    }

    @Test
    public void pingDestination() throws JCoException {
        JCoDestination dest = this.getDestination();
        dest.ping();
    }
}

代碼說明:

  • JCoDestinationManager.getDestination("ECC") 從 ECC.jcodestination 文件中獲取連接參數,實例化JCoDestination對象。這里有一個重要的約定,JCoDestinationManager.getDestination("ECC") 方法,在項目的根目錄中查找 ECC.jcodestination 文件,文件名為參數,在本例中即為 ECC, 文件的擴展名固定為 jcodestination。如果找到文件,從文件中獲取連接參數。這是 DestinationDataProvider 接口的一個默認實現,在開發和測試的時候還是很方便的,但如果在真實項目中使用,安全性和靈活性就不夠。本文的后面介紹介紹解決方法。

  • pingDestination() 方法調用 JcoDestination 對象的 ping() 方法測試與 SAP 系統的連接。

生成配置文件

剛才我們是手工編輯 ECC.jcodestination 文件,對于這個配置文件,很多連接參數來自于 DestinationDataProvider 接口,可以根據配置參數生成該配置文件,代碼如下:

package jco3.demo2;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import com.sap.conn.jco.ext.DestinationDataProvider;
import org.junit.Test;

public class DestinationFile {

    public Properties setProperties() {
        Properties props = new Properties();

        props.setProperty(DestinationDataProvider.JCO_ASHOST, "192.168.44.100");
        props.setProperty(DestinationDataProvider.JCO_SYSNR, "00"); // instance number
        props.setProperty(DestinationDataProvider.JCO_USER, "STONE");
        props.setProperty(DestinationDataProvider.JCO_PASSWD, "w123456");
        props.setProperty(DestinationDataProvider.JCO_CLIENT, "001");
        props.setProperty(DestinationDataProvider.JCO_LANG, "EN");

        return props;
    }

    private void doCreateFile(String fName, String suffix, Properties props) throws IOException {

        /**
         * Write contents of properties into a text file
         * which was named [fName+suffix.jcodestination]
         */

        File cfg = new File(fName + "." + suffix);
        if (!cfg.exists()){
            // Create file output stream, not using append mode
            FileOutputStream outStream = new FileOutputStream(cfg, false);

            // store the properties in file output stream
            // and also add comments
            props.store(outStream, "SAP Logon parameters");

            outStream.close();
        }else{
            throw new RuntimeException("Configuration file already exits.");
        }
    }

    @Test
    public void testCreateCfgFile() throws IOException {
        Properties props = this.setProperties();
        String fileName = "SAP_AS";

        this.doCreateFile(fileName, "jcodestination", props);
    }
}

代碼說明:

  • setProperties() 方法屬性參照 DestinationDataProvider 類的常量設置 Properties 對象,并且返回。
  • doCreateFile() 方法根據要求的文件名和擴展名在項目的根文件夾下,創建名為 SAP_AS.jcodestination 的文本文件,文件的內容就是 Properties 實例的內容。
  • testCreateCfgFile() 方法,調用上面的方法,創建配置文件。

自定義配置文件

我們看到,默認情況下,SAP 對配置文件的路徑擴展名都不能改變,如果我們想把文件放在任意位置,擴展名也使用其他的擴展名,有沒有辦法?答案是有,方法是實現 DestinationDataProvider 接口,并改寫 (override) getDestinationProperties() 方法,然后通過Environment.registerDestinationDataProvider() 方法進行注冊。OK, 一起來看看代碼:

第一步: 創建 DestinationDataProviderImpl 類,實現 DestinationDataProvider 接口;

DestinationDataProvider接口實現的代碼:

package jco3.demo3;

import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class DestinationDataProviderImpl implements DestinationDataProvider {
    private File dir;
    private String destName;
    private String suffix;

    @Override
    public Properties getDestinationProperties(String s) {
        Properties props = null;

        try {
            props = this.loadProperties();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return props;
    }

    @Override
    public boolean supportsEvents() {
        return false;
    }

    @Override
    public void setDestinationDataEventListener(DestinationDataEventListener destinationDataEventListener) {
        throw new UnsupportedOperationException();
    }

    public void setDestinationFile(File dir, String destName, String suffix) {
        /**
         * 指定 Destination file
         */

        this.dir = dir;
        this.destName = destName;
        this.suffix = suffix;
    }

    private Properties loadProperties() throws IOException {
        Properties props = null;

        File cfgFile = new File(this.dir, this.destName + "." + this.suffix);
        if (cfgFile.exists()){
            FileInputStream inputStream = new FileInputStream(cfgFile);
            props = new Properties();
            props.load(inputStream);
            inputStream.close();
        }else{
            throw new RuntimeException("Configuration file does not exits");
        }

        return props;
    }
}

第二步: 創建 FileDestinationManager類,在該類中,提供 getDestination() 方法,在方法中通過 Environment.registerDestinationDataProvider() 方法,將 DestinationDataProviderImpl 對象,注冊到 Environment。代碼如下:

package jco3.demo3;

import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.ext.Environment;

import java.io.File;

public class FileDestinationManager {
    public static JCoDestination getDestination(String destName) throws JCoException {
        File dir = new File("."); // current directory
        String suffix = "txt";

        DestinationDataProviderImpl providerImpl = new DestinationDataProviderImpl();
        providerImpl.setDestinationFile(dir, destName, suffix);
        Environment.registerDestinationDataProvider(providerImpl);

        JCoDestination dest = JCoDestinationManager.getDestination(destName);
        return dest;
    }
}

我們看到,getDestination 方法中,文件的路徑、文件名和擴展名,都是我們自己定義的。文件名通過參數來指定,從這里也可以看出,JCoDestinationManager.getDestination() 方法從哪里查找連接參數,取決于 Environment 注冊的 DestinationDataProvider 接口的實現

測試 FileDestinationDataManager, 代碼如下:

package jco3.demo3;

import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoException;
import org.junit.Test;

public class TestFileDestinationManager {
    @Test
    public void pingDest() throws JCoException {
        JCoDestination dest = FileDestinationManager.getDestination("SAP_AS");
        dest.ping();
    }
}

代碼中設置 JCoDestination 參數

還記得 NCo3.0 可以將連接 SAP 的參數寫在代碼中嗎? JCo3.0 也是可以的,方法的關鍵就是實現
DestinationDataProvider 接口,并改寫 getDestinationProperties() 方法
。不多說,上代碼。

第一步:實現 DestinationDataProvider 接口

package jco3.demo4;

import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class DestinationDataProviderImpl implements DestinationDataProvider {
    /**
     * DestinationDataProvider is of type interface.
     * We define DestinationDataProviderImpl class to implements this interface
     * so that we can define the SAP connection parameters more flexibly, 
     * not just in xxx.jcodestionation file.
     *
     * The point is that we override getDestinationProperties() method.
     * Afterwards, instance of DestinationDataProvider should be registered
     * using Environment.registerDestinationDataProvider() method to take effect
     */

    private Map provider = new HashMap();

    @Override
    public Properties getDestinationProperties(String destName) {
        if (destName == null){
            throw new NullPointerException("Destination name is empty.");
        }

        if (provider.size() == 0){
            throw new IllegalStateException("Data provider is empty.");
        }

        return (Properties) provider.get(destName);
    }

    @Override
    public boolean supportsEvents() {
        return false;
    }

    @Override
    public void setDestinationDataEventListener(DestinationDataEventListener destinationDataEventListener) {
        throw new UnsupportedOperationException();
    }

    public void addDestinationProps(String destName, Properties props){
        provider.put(destName, props);
    }
}

第二步:創建 DestinationManager 類,提供 getDestination() 方法,將 DestinationDataProviderImp 對象注冊到 Environment:

package jco3.demo4;

import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.Environment;

import java.util.Properties;

public class DestinationManager {
    private static Properties setProperties(String destName)
    {
        // SAP connection parameters and other properties
        Properties props = new Properties();

        if (destName == "SAP_AS") {
            props.setProperty(DestinationDataProvider.JCO_ASHOST, "192.168.44.100");
            props.setProperty(DestinationDataProvider.JCO_SYSNR, "00");
            props.setProperty(DestinationDataProvider.JCO_USER, "STONE");
            props.setProperty(DestinationDataProvider.JCO_PASSWD, "w123456");
            props.setProperty(DestinationDataProvider.JCO_CLIENT, "001");
            props.setProperty(DestinationDataProvider.JCO_LANG, "EN");
        }

        return props;
    }

    public static JCoDestination getDestination (String destName) throws JCoException {
        Properties props = setProperties(destName);

        DestinationDataProviderImpl providerImpl = new DestinationDataProviderImpl();
        providerImpl.addDestinationProps(destName, props);

        Environment.registerDestinationDataProvider(providerImpl);

        JCoDestination dest = JCoDestinationManager.getDestination(destName);
        return dest;
    }
}

測試 DestinationManager

package jco3.demo4;

import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoException;
import org.junit.Test;

public class TestDestinationManager {
    @Test
    public void pingDest() throws JCoException {
        JCoDestination dest = DestinationManager.getDestination("SAP_AS");
        dest.ping();
    }
}

源碼

github - sap_interface_jco3

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容