如何使用SAP jco登录多个SAP系统

How to login multiple SAP system using SAP jco

我是 SAP JCo 的新手,我需要使用 SAP Jco 调用多个 SAP 系统。但是我无法同时连接多个sap系统......

这是代码:

package com.sap.test;


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.JCoRepository;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.Environment;
import com.sap.utils.MyDestinationDataProvider;
import com.sap.utils.SapSystem;


public class TestMultipleSAPConnection {

    public static Properties properties;
    public static JCoDestination dest = null;
    public static JCoRepository repos = null;
    public static SapSystem system = null;
    String SAP_SERVER = "SAP_SERVER";
    MyDestinationDataProvider myProvider = null;



    public static void main(String[] args) throws JCoException {            
        getConnection_CRM();
        getConnection_R3();     
    }


public static JCoDestination getConnection_R3() {

        boolean connR3_flag = true;
        JCoDestination dest = null;
        JCoRepository repos = null;

        String SAP_SERVER = "SAP_SERVER";
        Properties properties = new Properties();
        SapSystem system = new SapSystem();

        system.setClient("100");
        system.setHost("r3devsvr.myweb.com");
        system.setLanguage("en");
        system.setSystemNumber("00");
        system.setUser("SAP-R3-USER");
        system.setPassword("init1234");

        properties.setProperty("jco.client.ashost", system.getHost());
        properties.setProperty("jco.client.sysnr", system.getSystemNumber());
        properties.setProperty("jco.client.client", system.getClient());
        properties.setProperty("jco.client.user", system.getUser());
        properties.setProperty("jco.client.passwd", system.getPassword());
        properties.setProperty("jco.client.lang", system.getLanguage());
        System.out.println("******* Connection Parameter Set *******");
        MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
        System.out.println("******* Destination Provider Set *******");
        myProvider.changePropertiesForABAP_AS(properties);
        if (!Environment.isDestinationDataProviderRegistered()) {
            System.out.println("Registering Destination Provider R3");
            Environment.registerDestinationDataProvider((DestinationDataProvider) myProvider);
        }else{
            System.out.println("Destination Provider already set..R3");
            connR3_flag = false;
        }
        try {
            dest = JCoDestinationManager.getDestination((String) SAP_SERVER);
            repos = dest.getRepository();
            if (repos == null) {
                System.out.println("Repos is null.....");
            } else {
                System.out.println("Repos is not null.....");
            }
            System.out.println("After getting repos...");
            if(connR3_flag){
               System.out.println("R3 Connection Successfull...");
            }
        } catch (Exception ex) {
            System.out.println(ex);
        }
        return dest;
    }

    public static JCoDestination getConnection_CRM() {
        boolean connCRM_flag = true;
        JCoDestination dest = null;
        JCoRepository repos = null;

        String SAP_SERVER = "SAP_SERVER";
        Properties properties = new Properties();
        SapSystem system = new SapSystem();

        system.setClient("200");
        system.setHost("crmdevsvr.myweb.com");
        system.setLanguage("en");
        system.setSystemNumber("00");
        system.setUser("SAP-CRM-USER");
        system.setPassword("init1234");

        properties.setProperty("jco.client.ashost", system.getHost());
        properties.setProperty("jco.client.sysnr", system.getSystemNumber());
        properties.setProperty("jco.client.client", system.getClient());
        properties.setProperty("jco.client.user", system.getUser());
        properties.setProperty("jco.client.passwd", system.getPassword());
        properties.setProperty("jco.client.lang", system.getLanguage());
        System.out.println("******* Connection Parameter Set *******");
        MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
        System.out.println("******* Destination Provider Set *******");
        myProvider.changePropertiesForABAP_AS(properties);
        if (!Environment.isDestinationDataProviderRegistered()) {
            System.out.println("Registering Destination Provider CRM");
            Environment.registerDestinationDataProvider((DestinationDataProvider) myProvider);
        }else{
            System.out.println("Destination Provider already set..CRM");
            connCRM_flag = false;
        }
        try {
            dest = JCoDestinationManager.getDestination((String) SAP_SERVER);
            repos = dest.getRepository();
            if (repos == null) {
                System.out.println("Repos is null.....");
            } else {
                System.out.println("Repos is not null.....");
            }
            System.out.println("After getting repos...");
            if(connCRM_flag){
               System.out.println("CRM Connection Successfull...");
            }
        } catch (Exception ex) {
            System.out.println(ex);
        }

        return dest;

    }

}

JCo JavaDoc 文档说:

Only one implementation of DestinationDataProvider can be registered. For registering another implementation the infrastructure has first to unregister the implementation that is currently registered. It is not recommended to permanently exchange DestinationDataProvider registrations. The one registered instance should globally manage all destination configurations for the whole infrastructure environment.

所以你必须注册一个 DestinationDataProvider 的实例,这是你的 class MyDestinationDataProvider 的一个实例。 此实施需要管理和存储所有 SAP 系统的所有不同登录配置,可通过不同的目标名称字符串访问。一个简单的 HashMap<String, Properties> 就足够了。将具有不同目标名称字符串的两个 Properties 实例添加到 HashMap 和 return 中,Properties 实例关联到从方法 MyDestinationDataProvider.getDestinationProperties(String destinationName) 传递的 destinationName。 因此,您可以通过其特定目标名称访问针对任何 SAP 系统的所需 JCoDestination 实例。在您的示例中,您将 "SAP_SERVER" 用于两个无效的目标配置。例如,改为使用 SAP 系统 ID (SID) 作为目标名称(键)。

如果使用您的示例中的名称,那么 JCoDestinationManager.getDestination("CRM") 将 return 系统 "CRM" 的 JCoDestination 实例和 JCoDestinationManager.getDestination("R3") JCoDestination 系统 "R3"。两者可以独立使用,也可以同时使用。就是这样。

与您分享我最近针对这个确切问题提出的解决方案。我发现了两种不同的方式来实现 CustomDestinationDataProvider,这样我就可以使用多个目的地。

我所做的有助于解决我的两个不同解决方案的事情是更改 CustomDestinationDataProvider 中实例化 MyDestinationDataProvider 内部 class 的方法,这样它就不会返回 ArrayList,而是 returns JCoDestination。我将此方法的名称从 executeSAPCall 更改为 getDestination。

我发现允许我使用多个目的地并成功更改目的地的第一种方法是为 MyDestinationDataProvider 引入一个 class 变量,以便我可以保留我的实例化版本。请注意,对于此解决方案,CustomDestinationDataProvider class 仍然嵌入在我的 java 应用程序代码中。

我发现此解决方案仅适用于一个应用程序。我无法在同一 tomcat 服务器上的多个应用程序中使用此机制,但至少我终于能够成功切换目的地。这是第一个解决方案 CustomDestinationDataProvider.java 的代码:

public class CustomDestinationDataProvider {

private MyDestinationDataProvider gProvider;    // class version of MyDestinationDataProvider

public class MyDestinationDataProvider implements DestinationDataProvider {
private DestinationDataEventListener eL;
private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
public Properties getDestinationProperties(String destinationName) {
    try {
        Properties p = secureDBStorage.get(destinationName);
        if(p!=null) {
            if(p.isEmpty())
                throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
            return p;
        }

        return null;
    } catch(RuntimeException re) {
        System.out.println("getDestinationProperties: Exception detected!!! message = " + re.getMessage());
        throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
    }
}
public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
    this.eL = eventListener;
}
public boolean supportsEvents() {
    return true;
}
public void changeProperties(String destName, Properties properties) {
    synchronized(secureDBStorage) {
        if(properties==null) {
            if(secureDBStorage.remove(destName)!=null) {
                eL.deleted(destName);
            }
        } else {
            secureDBStorage.put(destName, properties);
            eL.updated(destName); // create or updated
        }
    }
}
}
public JCoDestination getDestination(String destName, Properties connectProperties) {
MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
if (!destinationDataProviderRegistered) {
    try {
        com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
        gProvider = myProvider; // save our destination data provider in the class var
    } catch(IllegalStateException providerAlreadyRegisteredException) {
        throw new Error(providerAlreadyRegisteredException);
    }
} else {
    myProvider = gProvider; // get the destination data provider from the class var.
}
myProvider.changeProperties(destName, connectProperties);
JCoDestination dest = null;
try {
    dest = JCoDestinationManager.getDestination(destName);
} catch(JCoException e) {
    e.printStackTrace();
} catch (Exception e) {
    // TODO Auto-generated catch block
}
return dest;

} } 这是我的 servlet class 中的代码,我用它在我的应用程序代码中实例化和调用 CustomDestinationDataProvider:

CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
SAPDAO sapDAO = new SAPDAO();

Properties p1 = getProperties("SAPSystem01");
Properties p2 = getProperties("SAPSystem02");
try {
    JCoDestination dest = cddp.getDestination("SAP_R3_USERID_01", p1);  // establish the first destination
    sapDAO.searchEmployees(dest, searchCriteria);   // call the first BAPI
    dest = cddp.getDestination("SAP_R3_USERID_02", p2); // establish the second destination
    sapDAO.searchAvailability(dest);    // call the second BAPI
} catch (Exception e) {
    e.printStackTrace();
}

同样,此解决方案仅适用于一个应用程序。如果将此代码直接实现到多个应用程序中,第一个调用此代码的应用程序将获取资源,而另一个应用程序将出错。

我提出的第二个解决方案允许多个 java 应用程序同时使用 CustomDestinationDataProvider class。为了创建 jar,我将 CustomDestinationDataProvider class 从我的应用程序代码中分离出来,并为其创建了一个单独的 java spring 应用程序(不是 Web 应用程序)。然后,我将 MyDestinationDataProvider 内部 class 转换为单例。下面是 CustomDestinationDataProvider 单例版本的代码:

public class CustomDestinationDataProvider {
public static class MyDestinationDataProvider implements DestinationDataProvider {

////////////////////////////////////////////////////////////////////
// The following lines convert MyDestinationDataProvider into a singleton. Notice 
// that the MyDestinationDataProvider class has now been declared as static.
private static MyDestinationDataProvider myDestinationDataProvider = null;
private MyDestinationDataProvider() {
}
public static MyDestinationDataProvider getInstance() {
    if (myDestinationDataProvider == null) {
        myDestinationDataProvider = new MyDestinationDataProvider();
    }
    return myDestinationDataProvider;
}
////////////////////////////////////////////////////////////////////

private DestinationDataEventListener eL;
private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
public Properties getDestinationProperties(String destinationName) {
    try {
        Properties p = secureDBStorage.get(destinationName);
        if(p!=null) {
            if(p.isEmpty())
                throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
            return p;
        }
        return null;
    } catch(RuntimeException re) {
        throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
    }
}
public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
    this.eL = eventListener;
}
public boolean supportsEvents() {
    return true;
}
public void changeProperties(String destName, Properties properties) {
    synchronized(secureDBStorage) {
        if(properties==null) {
            if(secureDBStorage.remove(destName)!=null) {
                eL.deleted(destName);
            }
        } else {
            secureDBStorage.put(destName, properties);
            eL.updated(destName); // create or updated
        }
    }
}
}
public JCoDestination getDestination(String destName, Properties connectProperties) throws Exception {
MyDestinationDataProvider myProvider = MyDestinationDataProvider.getInstance();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
if (!destinationDataProviderRegistered) {
    try {
        com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
    } catch(IllegalStateException providerAlreadyRegisteredException) {
        throw new Error(providerAlreadyRegisteredException);
    }
}
myProvider.changeProperties(destName, connectProperties);
JCoDestination dest = null;
try {
    dest = JCoDestinationManager.getDestination(destName);
} catch(JCoException ex) {
    ex.printStackTrace();
    throw ex;
} catch (Exception ex) {
    ex.printStackTrace();
    throw ex;
}
return dest;
}
}

将此代码放入 jar 文件应用程序并创建 jar 文件(我称之为 JCOConnector.jar)后,我将 jar 文件放在 class 我 [=71] 的共享库路径中=] 服务器并重新启动 tomcat 服务器。就我而言,这是 /opt/tomcat/shared/lib。检查 /opt/tomcat/conf/catalina.properties 文件中的 shared.loader 行,了解共享库 class 路径的位置。我的看起来像这样:

shared.loader=\
${catalina.home}/shared/lib\*.jar,${catalina.home}/shared/lib

我还将这个 jar 文件的副本放在我工作站上的 "C:\Users\userid\Documents\jars" 文件夹中,以便测试应用程序代码可以看到 jar 中的代码并进行编译。然后我在我的两个测试应用程序的 pom.xml 文件中引用了这个 jar 文件的副本:

<dependency>
    <groupId>com.mycompany</groupId>
    <artifactId>jcoconnector</artifactId>
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>C:\Users\userid\Documents\jars\JCOConnector.jar</systemPath>
</dependency>

将其添加到 pom.xml 文件后,我右键单击每个项目,选择 Maven -> 更新项目...,然后再次右键单击每个项目并选择 'Refresh' .我学到的非常重要的一点是不要将 JCOConnector.jar 的副本直接添加到我的任何一个测试项目中。这样做的原因是因为我想使用 /opt/tomcat/shared/lib/JCOConnector.jar 中的 jar 文件中的代码。然后我构建并部署了我的每个测试应用程序到 tomcat 服务器。

在我的第一个测试应用程序中调用我的 JCOConnector.jar 共享库的代码如下所示:

CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
JCoDestination dest = null;
SAPDAO sapDAO = new SAPDAO();

Properties p1 = getProperties("SAPSystem01");
try {
    dest = cddp.getDestination("SAP_R3_USERID_01", p1);
    sapDAO.searchEmployees(dest);
} catch (Exception ex) {
    ex.printStackTrace();
}

我的第二个测试应用程序调用我的 JCOConnector.jar 共享库的代码如下所示:

CustomDestinationDataProvider cddp = new CustomDestinationDataProvider();
JCoDestination dest = null;
SAPDAO sapDAO = new SAPDAO();

Properties p2 = getProperties("SAPSystem02");
try {
    dest = cddp.getDestination("SAP_R3_USERID_02", p2);
    sapDAO.searchAvailability(dest);
} catch (Exception ex) {
    ex.printStackTrace();
}

我知道我省略了很多在您的工作站和服务器上安装 SAP JCO 3 库时涉及的步骤。我确实希望这至少能帮助另一个人克服试图在同一台服务器上与 SAP 对话的多个 spring mvc java 应用程序。