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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容