Как войти в несколько систем SAP с использованием SAP Jco
Я новичок в SAP JCo. У меня есть требование для вызова нескольких систем SAP с использованием SAP Jco. Но я не могу подключить несколько систем 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;
}
}
2 ответа
Документация JCo JavaDoc гласит:
Только одна реализация DestinationDataProvider может быть зарегистрирована. Для регистрации другой реализации инфраструктура должна сначала отменить регистрацию реализации, которая в данный момент зарегистрирована. Не рекомендуется постоянно обмениваться регистрациями DestinationDataProvider. Один зарегистрированный экземпляр должен глобально управлять всеми конфигурациями назначения для всей среды инфраструктуры.
Таким образом, вы должны зарегистрировать один экземпляр DestinationDataProvider
это экземпляр вашего класса MyDestinationDataProvider
, Эта реализация должна управлять и хранить ВСЕ различные конфигурации входа в систему для всех ваших систем SAP, доступные через отдельную строку имени назначения. Просто HashMap<String, Properties>
будет достаточной формой хранения для этого. Добавьте два Properties
экземпляры с различными строками имен назначения в HashMap
и вернуть Properties
экземпляр, связанный с переданным destinationName из метода MyDestinationDataProvider.getDestinationProperties(String destinationName)
, Таким образом, вы можете получить доступ к желаемой JCoDestination
экземпляр, нацеленный на любую систему SAP через его конкретное имя назначения. В вашем примере вы использовали "SAP_SERVER" для обеих конфигураций назначения, которые не будут работать. Например, вместо этого используйте идентификатор системы SAP (SID) в качестве имени пункта назначения (ключа).
Если использовать имена из вашего примера, то JCoDestinationManager.getDestination("CRM")
вернет JCoDestination
экземпляр для системы "CRM" и JCoDestinationManager.getDestination("R3")
JCoDestination
для системы "R3". И то, и другое можно использовать независимо и одновременно. Вот и все.
Я поделился с вами решением, которое я недавно придумал для этой проблемы. Я обнаружил два разных способа реализации CustomDestinationDataProvider, чтобы я мог использовать несколько мест назначения.
Что-то, что я сделал, что помогло с обоими моими различными решениями, это изменил метод в CustomDestinationDataProvider, который создает экземпляр внутреннего класса MyDestinationDataProvider, так что вместо возврата ArrayList он возвращает JCoDestination. Я изменил имя этого метода с executeSAPCall на getDestination.
Первый способ, который я обнаружил и позволил мне использовать несколько мест назначения, успешно меняя места назначения, заключался в том, чтобы ввести переменную класса для MyDestinationDataProvider, чтобы я мог сохранить свою экземплярную версию. Обратите внимание, что для этого решения класс CustomDestinationDataProvider по-прежнему встроен в код моего приложения 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;
}} Это код в моем классе сервлетов, который я использую для создания экземпляра и вызова 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. Я вырвал класс CustomDestinationDataProvider из кода своего приложения и создал для него отдельное приложение Java Spring (не веб-приложение) с целью создания jar-файла. Затем я преобразовал внутренний класс MyDestinationDataProvider в синглтон. Вот код для одиночной версии 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 в путь к классам общей библиотеки моего сервера tomcat и перезапустил сервер tomcat. В моем случае это был /opt/tomcat/shared/lib. Проверьте файл /opt/tomcat/conf/catalina.properties для строки shared.loader, где находится путь к классу вашей общей библиотеки. Моя выглядит так:
shared.loader=\
${catalina.home}/shared/lib\*.jar,${catalina.home}/shared/lib
Я также поместил копию этого файла JAR в папку "C:\Users\userid\Documents\jars" на моей рабочей станции, чтобы код тестового приложения мог видеть код в банке и компилировать. Затем я сослался на эту копию файла jar в моем файле pom.xml в обоих моих тестовых приложениях:
<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 -> Обновить проект..., а затем снова щелкнул правой кнопкой мыши по каждому проекту и выбрал "Обновить". Что-то очень важное, что я узнал, это не добавлять копию JCOConnector.jar напрямую ни в один из моих тестовых проектов. Причина этого в том, что я хочу использовать код из файла jar в /opt/tomcat/shared/lib/JCOConnector.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, установленной на вашей рабочей станции и сервере. Я действительно надеюсь, что это поможет по крайней мере еще одному человеку преодолеть трудности, связанные с попытками получить несколько Java-приложений Spring MVC, говорящих с SAP на одном сервере.