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

如果安装成功,应该显示类似如下界面:

SAP接口编程 之 JCo3.0 系列 (01):JCoDestination_第1张图片
jco3安装成功的显示界面

JCoDestination 对象介绍

JCoDestination 代表后端 SAP 系统,开发人员不必关心与 SAP 的连接,JCo3.0 运行时环境负责连接的管理,包括连接和释放连接。我们先看一个简单的例子,了解 JCo3.0 JCoDestination 类的一些要点。

我使用的编程环境是 Eclipse,环境准备如下:


  • 新建一个 Java 项目,项目名为 JCo3Demo
  • sapjco3.jar 加入到项目的 Build Path 中。sapjco3.jar 和 sapjco3.dll 要放在同一个文件夹下。
  • 在 Eclipse 项目文件夹下,新建一个文本文件,文件名命名为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接口编程 之 JCo3.0 系列 (01):JCoDestination_第2张图片
SAP GUI

环境准备好了,先来一段最简单的代码,测试是否可以连接到 SAP 系统:

package jco3.demo1;

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

public class JCoDestinationDemo
{
    public JCoDestination getDestination() throws JCoException
    {
        /**
         * Get instance of JCoDestination from file named ECC.jcodestination
         * which should be located in the installation folder of java project
         */
         
        JCoDestination dest = JCoDestinationManager.getDestination("ECC");
        return dest;
    }
    
    @Test
    public void pingDestination() throws JCoException
    {    
        JCoDestination dest = this.getDestination();
        dest.ping();
    }   
}

代码说明:


getDestination() 方法


getDestination()方法中,JCoDestinationManager.getDestination("ECC") 从 ECC.jcodestination
文件获取连接参数,创建 JCoDestination 对象的实例。

这里有一个重要的约定,JCoDestinationManager.getDestination("ECC")方法,从 Eclipse 项目的根目录,查找 ECC.jcodestination 文件,文件的扩展名必须固定为 jcodestination,文件名就是 getDestination 方法的参数。如果找到文件,从文件中获取连接参数。这是 DestinationDataProvider 接口的一个默认实现,在开发和测试的时候还是很方便的,但如果在真实项目中使用,安全性和灵活性就不够。后续介绍解决方法。


pingDestination() 方法


pingDestination() 方法调用 JcoDestination 对象的 ping() 方法测试与 SAP 系统的连接。

生成配置文件

刚才我们是手工编辑 ECC.jcodestination 文件,因为很多连接参数来自于 DestinationDataProvider 接口,如果想通过代码来创建配置文件,可以使用如下代码:

package jco3.demo2;

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

public class DestinationFile
{
    private Properties setProperties()
    {       
        // logon parameters and other properties
        Properties connProps = new Properties();
        connProps.setProperty(DestinationDataProvider.JCO_ASHOST, "192.168.65.100");
        connProps.setProperty(DestinationDataProvider.JCO_SYSNR, "00");
        connProps.setProperty(DestinationDataProvider.JCO_USER, "STONE");
        connProps.setProperty(DestinationDataProvider.JCO_PASSWD, "xxxxxx");
        connProps.setProperty(DestinationDataProvider.JCO_CLIENT, "001");
        connProps.setProperty(DestinationDataProvider.JCO_LANG, "EN");

        return connProps;
    }
    
    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()){ // file not exists
            // Create file output stream, not using append mode
            FileOutputStream fOutputStream = new FileOutputStream(cfg, false); 
            
            // store the properties in file output stream
            // and also add comments
            props.store(fOutputStream, "SAP logon parameters:"); 
            
            fOutputStream.close();
        }else{
            throw new RuntimeException("File alreay exists.");
        }
    }
    
    @Test
    public void createConfigFile() throws IOException
    {       
        Properties props = this.setProperties();
        String fileName = "SAP_AS"; // sap application server
        
        // jcodestination suffix is required by JCoDestinationManager
        this.doCreateFile(fileName, "jcodestination", props);
    }
}

代码说明:

  • setProperties() 方法属性参照 DestinationDataProvider 类的常量设置Properties 的实例。
  • doCreateFile() 方法根据需求的文件名和扩展名在 Eclipse 项目的根文件夹下,创建一个文本文件,文件的内容就是 Properties 实例的内容。
  • createConfigFile() 方法,调用上面的两个方法,创建配置文件。

更改配置文件名的路径和扩展名

我们看到,默认情况下,SAP 对配置文件的路径和扩展名都不能改变,如果我们想把文件放在任意位置,扩展名也使用其他的扩展名,有没有办法?答案是有,方法是实现 DestinationDataProvider 接口,并改写
(override) getDestinationProperties() 方法
,然后通过Environment.registerDestinationDataProvider() 方法进行注册。

OK, 一起来看看代码,代码分为三个部分:

一、 创建 FileDestinationDataProviderImp 类,实现
DestinationDataProvider 接口;

DestinationDataProvider接口实现的代码:

package jco3.demo2;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;

public class FileDestinationDataProviderImp implements DestinationDataProvider
{   
    private File dir;
    private String destName; // destination name
    private String suffix;
    
    public void setDestinationFile(File dir, String destName, String suffix)
    {
        this.dir = dir;
        this.destName = destName;
        this.suffix = suffix;
    }
    
    private Properties loadProperties(File dir, String destName, String suffix) throws IOException
    {
        Properties props = null;
        
        // create a file with name: fullName in destDirectory
        File destFile = new File(dir, destName+"."+suffix);
        
        if (destFile.exists()){
            FileInputStream fInputStream = new FileInputStream(destFile);
            props = new Properties();
            props.load(fInputStream);
            fInputStream.close();           
        }else{
            throw new RuntimeException("Destination file does not exist.");
        }   
        
        return props;
    }

    @Override
    public Properties getDestinationProperties(String destName)
    {       
        Properties props = null;
        
        try {
            props = this.loadProperties(this.dir, this.destName, this.suffix);
        } catch (IOException e) {           
            e.printStackTrace();
        }       

        return props;
    }

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

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

二、 创建 FileDestinationDataProvider类,注册FileDestinationDataProviderImp 的实例,并提供 getDestination() 方法供调用。代码如下:

package jco3.demo2;

import java.io.File;
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;

public class FileDestinationDataProvider
{   
    public static JCoDestination getDestination() throws JCoException   
    {   
        File directory = new File("."); // current directory;
        String fileName = "SAP_AS";
        String suffix = "txt";
        
        FileDestinationDataProviderImp destDataProvider = new FileDestinationDataProviderImp();
        destDataProvider.setDestinationFile(directory, fileName, suffix);       
        Environment.registerDestinationDataProvider(destDataProvider);
        
        JCoDestination dest = JCoDestinationManager.getDestination(fileName);
        
        return dest;        
    }
}

我们看到,getDestination 方法中,文件的路径,文件的扩展名,都是我们自己定义的。文件名作为 JCoDestinationManager.getDestination 方法的 destination name 参数。从这里也可可以看到,JCoDestinationManager.getDestination 方法从哪里查找连接参数,是依赖于 Environment 注册的 DestinationDataProvider 实现

三、调用 FileDestinationDataProvider 类的 getDestination() 方法。代码如下:

package jco3.demo2;

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

public class TestFileDestinationProvider
{
    @Test
    public void pingSAPDestination() throws JCoException
    {
        JCoDestination dest = FileDestinationDataProvider.getDestination();
        dest.ping();
    }
}

DestinationDataProvider 另一种实现

记得 NCo3.0 可以将登陆参数写在代码中吗? 如果我们也想将连接参数直接写在代码中,怎么做呢?刚才说过了,关键就是实现
DestinationDataProvider 接口,并改写 getDestinationProperties() 方法
。不多说,上代码。

第一部分:DestinationDataProvider 接口实现

package jco3.demo3;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;

public class DestinationDataProviderImp implements DestinationDataProvider
{
    /**
     * DestinationDataProvider is an interface
     * We define DestinationDataProviderImp class to implements this interface
     * so that we can define the logon parameters more flexibly
     * not just in xxx.jcodestionation file.
     * 
     * The key point is that we override getDestinationProperties() method
     * Afterwards, instance of DestinationDataProvider should be registered
     * using Environment.registerDestinationDataProvider() method to take effect
     */
    
    @SuppressWarnings("rawtypes")
    private Map provider = new HashMap();
    
    @SuppressWarnings("unchecked")
    public void addDestinationProperties(String destName, Properties props)
    {
        provider.put(destName, props);
    }

    @Override
    public Properties getDestinationProperties(String destName)
    {       
        if (destName == null){
            throw new NullPointerException("Destinantion name is empty.");
        }
        
        if (provider.size() == 0){
            throw new IllegalStateException("Data provider is empty.");
        }
        
        return (Properties) provider.get(destName);
    }

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

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

第二部分:创建 DestinationProivder 类,提供 getDestination() 方法,注册 DestinationDataProviderImp 类的实例:

package jco3.demo3;

import java.util.Properties;
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;

public class DestinationProvider
{   
    private static Properties setProperties()
    {   
        // logon parameters and other properties
        Properties connProps = new Properties();        
        connProps.setProperty(DestinationDataProvider.JCO_ASHOST, "192.168.65.100");
        connProps.setProperty(DestinationDataProvider.JCO_SYSNR, "00");
        connProps.setProperty(DestinationDataProvider.JCO_USER, "STONE");
        connProps.setProperty(DestinationDataProvider.JCO_PASSWD, "xxxxxx");
        connProps.setProperty(DestinationDataProvider.JCO_CLIENT, "001");
        connProps.setProperty(DestinationDataProvider.JCO_LANG, "EN");
        
        return connProps;       
    }
    
    public static JCoDestination getDestination() throws JCoException
    {
        String destName = "SAP_AS";
        
        Properties props = setProperties();     
        DestinationDataProviderImp destDataProvider = new DestinationDataProviderImp();
        destDataProvider.addDestinationProperties(destName, props);
        Environment.registerDestinationDataProvider(destDataProvider);
        
        JCoDestination dest = JCoDestinationManager.getDestination(destName);
        return dest;        
    }
}

第三部分:测试 DestinationProvidergetDestination() 方法:

package jco3.demo3;

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

public class TestDestionProvider
{
    @Test
    public void pingSAPDestination() throws JCoException
    {
        JCoDestination dest = DestinationProvider.getDestination();
        dest.ping();
    }
}

你可能感兴趣的:(SAP接口编程 之 JCo3.0 系列 (01):JCoDestination)