zoukankan      html  css  js  c++  java
  • JCO 连接SAP

    JCO 连接SAP

    2月4日
    033-使用JCo远程调用SAP系统函数
    要用Java程序远程调用ABAP函数可以使用SAP提供的RFC针对Java程序的API——JCo。
    0.JCo的安装很不幸,虽然JCo是用Java语言编写的但却是平台相关的。(具体来说,JCo的Java包sapjco.jar确实是平台无关的,但是运行JCo需要RFC库支持,而RFC库是平台相关的)
    目前为止我还只有在Windows系统中安装的版本,因为JCo不是一个开源组件,甚至在SAP官方下载它是需要SAP Service MarketPlace的用户帐号的。目前我手上的是JCo2.1.6Windows版本。
    除了API文档和一些DEMO程序,你需要用到的有3个文件:librfc32.dll,sapjcorfc.dll,sapjco.jar。
    安装配置步骤如下:
    (1).将sapjco.jar导入到你的项目库路径中。SAP官方的指导是要你配置CLASSPATH,这种方法在我看来不值得提倡,因为eclipse或netBeans这样的IDE可以帮你轻松导入jar库而无需摆弄系统环境变量。
    (2).将sapjcorfc.dll文件拷贝至sapjco.jar所在的文件夹中。注意,这两个文件必须放在同一个文件夹里,它们俩必须像热恋中的情人一样一直黏在一起才能使JCo正常工作。
    (3). 将librfc32.dll文件拷贝至C:WINDOWSsystem32中。如果系统中已安装过SAP客户端,那么可能本来就已经有一个 librfc32.dll在那里了,SAP官方要求你覆盖那个文件,这是一种很不负责的方法。当你覆盖这个文件后很可能你的SAPLogon就无法工作 了。正确的做法是备份原来的文件,然后尝试覆盖后使用SAPLogon,如果出问题了,把原来那个文件找回来。我在安装了710 Final Release版本的SAPLogon机器上使用原来的librfc32.dll运行JCo目前还没有发生什么异常。
    好了,到现在为止,JCo算是安装好了,我们可以开始编写程序了。
    1.使用JCo调用远程ABAP函数
    使 用Java程序远程调用ABAP函数的大致流程是这样地:先要有SAP系统所在服务器的IP地址、要登录的SAP系统的客户端号、系统编号、用户名、用户 密码,有了这些后,就可以建立一个到该SAP系统的连接,然后在这个连接上调用允许远程调用的函数,得到调用后的结果,关闭连接。
    这其中牵涉到一些细节,我们先看程序:
    package jcousage;
    import com.sap.mw.jco.IFunctionTemplate;
    import com.sap.mw.jco.JCO;
    import java.util.Properties;
    public class TestJCo {
    public static void main(String[] args) {
    Properties logonProperties = new Properties();
    logonProperties.put("jco.client.ashost","192.168.1.123"); //系统的IP地址
    logonProperties.put("jco.client.client","800"); //要登录的客户端
    logonProperties.put("jco.client.sysnr","00"); //系统编号
    logonProperties.put("jco.client.user","young98"); //登录用户名
    logonProperties.put("jco.client.passwd","password"); //用户登录口令
    //用上述条件创建一个连接对象
    JCO.Client myConnection = JCO.createClient( logonProperties );
    myConnection.connect(); //进行实际连接
    //如果连接不为null并且处于活动状态
    if (myConnection != null && myConnection.isAlive()) {
    //从连接获得一个逻辑意义上的“仓库”对象(Repository)
    JCO.Repository myRepository =
    new JCO.Repository("Repository", //只是一个名字
    myConnection); //活动的连接
    //要调用的SAP函数名称
    String strFunc = "BAPI_FLIGHT_GETLIST";
    //从“仓库”中获得一个指定函数名的函数模板
    IFunctionTemplate ft = myRepository.getFunctionTemplate(strFunc.toUpperCase());
    //从这个函数模板获得该SAP函数的对象
    JCO.Function function = ft.getFunction();
    //获得函数的import参数列表
    JCO.ParameterList input = function.getImportParameterList();
    //设定一个import参数的值。参数名为“MAX_ROWS”,设定值为10
    input.setValue(10, "MAX_ROWS");
    //如果参数是一个结构,用参数名获得一个对应类型的结构对象
    JCO.Structure sFrom = input.getStructure("DESTINATION_FROM");
    //设定结构中的变量。变量名为“CITY”,设定值为“NEW YORK”
    sFrom.setValue("NEW YORK", "CITY");
    //将处理好的结构赋给该参数
    input.setValue(sFrom, "DESTINATION_FROM");
    //用参数名获得一个对应类型的内部表对象
    JCO.Table tDateRange = function.getTableParameterList().getTable("DATE_RANGE");
    //接下来基本属于体力活了......
    //新增一条空行
    tDateRange.appendRow();
    //定位到第0行
    tDateRange.setRow(0);
    //设定该行对应变量
    tDateRange.setValue("I", "SIGN");
    tDateRange.setValue("EQ", "OPTION");
    tDateRange.setValue("20040330", "LOW");
    //新增一条空行
    tDateRange.appendRow();
    //定位到第1行
    tDateRange.setRow(1);
    //设定该行对应变量
    tDateRange.setValue("I", "SIGN");
    tDateRange.setValue("EQ", "OPTION");
    tDateRange.setValue("20040427", "LOW");
    //......
    //执行函数
    myConnection.execute(function);
    //在执行函数后可用相同方式获得输出结果
    JCO.Table flights = function.getTableParameterList().getTable("FLIGHT_LIST");
    //JCO.Table对象可以直接输出到html文件
    flights.writeHTML("C:/function.html");
    //也可以如下单独获得表中个别变量
    System.out.println("Airline" + "tt"
    + "from city" + "t"
    + "to city" + "tt"
    + "departure time" + "tt"
    + "price" + "t"
    + "CURR");
    for (int i = 0; i < flights.getNumRows(); i++) {
    flights.setRow(i);
    System.out.println(flights.getString("AIRLINE") + "t"
    + flights.getString("CITYFROM") + "t"
    + flights.getString("CITYTO") + "t"
    + flights.getDate("FLIGHTDATE") + "t"
    + flights.getDouble("PRICE") + "t"
    + flights.getString("CURR"));
    }
    //断开连接
    myConnection.disconnect();
    } else {
    System.out.println(false);
    }
    }
    }//:~
    首先,将连接需要的信息放到一个Properties里,对应的键值都是固定的。需要注意的是给定的用户名必须有足够的权限进行远程调用。
    JCO.Client myConnection = JCO.createClient( logonProperties );
    这样就创建了一个到SAP系统的连接对象了。接着只要在这个对象上调用connect()方法就能激活连接。为了保证这个连接确实可用,加上这一句:
    if (myConnection != null && myConnection.isAlive())
    接下来比较麻烦,先要从这个活动的连接上拿到一个“仓库”,然后从这个“仓库”里拿到函数模板,再从函数模板里拿到对应函数对象。
    JCO.Repository myRepository =
    new JCO.Repository("Repository", //只是一个名字
    myConnection); //活动的连接
    String strFunc = "BAPI_FLIGHT_GETLIST";
    IFunctionTemplate ft = myRepository.getFunctionTemplate(strFunc.toUpperCase());
    JCO.Function function = ft.getFunction();
    整个过程很唬人,其实也就那么回事。这里我们调用的是一个叫“BAPI_FLIGHT_GETLIST”函数,这个函数在SAP系统中被声明为Remote-Enabled Module,能够被远程调用的函数都必须声明为Remote-Enabled Module。
    好了,我们现在有函数对象了。我们知道,SAP函数主要就是有5个输入输出接口:Import、Export、Changing、Tables、Exceptions。其中除了Changing在远程调用中是不被允许的外,其他都是可用的属性:
    Import:通过在函数对象上调用getImportParameterList()方法获得Import变量列表。
    JCO.ParameterList input = function.getImportParameterList();
    Export:在函数被执行后通过在函数对象上调用getExportParameterList()方法获得Export变量列表。
    JCO.ParameterList output = function.getExportParameterList();
    Changing:不支持。
    Tables:通过在函数对象上调用getTableParameterList()方法获得Tables变量列表。
    JCO.ParameterList tables = function.getTableParameterList();
    Exceptions:可通过在函数对象上调用getExceptionList()方法获得Exceptions变量数组,也可调用getException(java.lang.String key)获得单个异常对象。
    JCO.AbapException[] abapExceptions = function.getExceptionList();
    JCO.AbapException abapException = function.getException( "1" );
    Import、Export、Tables所获得的都是JCO.ParameterList对象。
    如果参数是基本类型,则可直接设定:
    input.setValue(10, "MAX_ROWS");
    Object object = output.getValue("MAX_ROWS");
    当然你这样getValue得到的只能是一个Object对象,如果你知道你会得到什么类型(一般情况下你应该知道),你可以直接get这个类型。比如,我们知道"MAX_ROWS"是个int值,所以:
    int maxRows = output.getInt("MAX_ROWS");
    如果参数是一个结构(如果你是个面向对象的程序员,请回忆一下C语言中的结构变量,谢谢),那么你得先得到一个对应此参数的结构对象,然后对结构中的变量成员get或者set:
    JCO.Structure sFrom = input.getStructure("DESTINATION_FROM");
    sFrom.setValue("NEW YORK", "CITY");
    当然如果是Import的话最后别忘了把这个结构放回列表中:
    input.setValue(sFrom, "DESTINATION_FROM");
    在对Tables的参数进行操作时,也得先得到一个与该参数内部表对应的内部表对象:
    JCO.Table tDateRange = tables.getTable("DATE_RANGE");
    现在这个内部表对象还是空的,所以要先加一行数据,然后将一个逻辑指针指向该行,再进行各个元素的赋值或取值:
    tDateRange.appendRow();
    tDateRange.setRow(0);
    tDateRange.setValue("I", "SIGN");
    tDateRange.setValue("EQ", "OPTION");
    tDateRange.setValue("20040330", "LOW");
    内部表对象支持直接将表内容发送到HTML文件:
    JCO.Table flights = function.getTableParameterList().getTable("FLIGHT_LIST");
    flights.writeHTML("C:/function.html");
    想知道内部表中有几条数据,可以调用getNumRows()方法:
    int rows = flights.getNumRows();
    结束调用时要记得断开连接:
    myConnection.disconnect();
    2.使用连接池很多时候我们需要进行大量连接,SAP系统本来就脆弱,连接再一多那真的是老牛拉车了。所以我们要用连接池进行连接。下面是一个使用连接池的例子:
    package jcousage;
    import com.sap.mw.jco.JCO;
    import java.util.Properties;
    public class UsageForPool {
    public static void createConnectionPool( String poolName,
    int maxConnection,
    Properties logonProperties) {
    JCO.Pool pool = JCO.getClientPoolManager().getPool(poolName);
    if(pool == null) {
    JCO.addClientPool( poolName, // 连接池名
    maxConnection, // 最大连接数
    logonProperties ); // logon设定参数
    }
    }
    public static JCO.Client getConnectionInPool( String poolName ) {
    JCO.Client connection = null;
    JCO.Pool pool = JCO.getClientPoolManager().getPool(poolName);
    if(pool != null) {
    connection = JCO.getClient(poolName);
    }
    return connection;
    }
    public static void releaseConnection( JCO.Client connection ) {
    JCO.releaseClient( connection );
    }
    public static void removeConnectionPool(String poolName) {
    JCO.removeClientPool(poolName);
    }
    public static void main(String[] args) {
    Properties logonProperties = new Properties();
    logonProperties.put("jco.client.ashost","192.168.1.123");
    logonProperties.put("jco.client.client","800");
    logonProperties.put("jco.client.sysnr","00");
    logonProperties.put("jco.client.user","young98");
    logonProperties.put("jco.client.passwd","password");
    String poolName = "ThePool";
    createConnectionPool(poolName, 6, logonProperties);
    JCO.Client connection = getConnectionInPool(poolName);
    connection.connect();
    if (connection != null && connection.isAlive()) {
    System.out.println("Connection is alive!");
    releaseConnection(connection);
    removeConnectionPool(poolName);
    } else {
    System.out.println("Connection FALSE!");
    }
    }
    }
    首先,我们仍然需要一个Properties对象用来建立连接,不过这次是建立的连接是一个连接池:
    JCO.addClientPool( poolName, maxConnection, logonProperties );
    这个方法同时还需要池的名字以及最大连接数一起作为参数。当然在新建一个连接池之前,检查一下有没有相同名字的池已经存在是一个好习惯。
    有了池我们就可以在连接池里获得连接啦:
    JCO.Client connection = JCO.getClient(poolName);
    然后在这个连接对象上激活连接就可以啦:
    connection.connect();
    接下来就跟上面说的一样用啦,简单吧。
    用完别忘了释放连接:
    JCO.releaseClient( connection );
  • 相关阅读:
    20170930-构建之法:现代软件工程-阅读笔记
    结对-四则运算答题器-开发过程
    小米3Android开发学习笔记三
    小米3Android开发学习笔记二
    小米3Android开发学习笔记一
    maya学习之晶格变形
    只说说C++内联函数
    Flash 文件加载方案以及一些问题及解决(1)
    博客内容笔记
    Unity里关于[HideInInspector]23事
  • 原文地址:https://www.cnblogs.com/anuoruibo/p/3521909.html
Copyright © 2011-2022 走看看