在 Tomcat 服务器上的应用程序中实现不同的目的地
Implementing different destinations in applications on the Tomcat server
今年早些时候,我为我的一个 Spring MVC tomcat 应用程序开发了 SAP JCO CustomDestinationProvider
的实现。在我的应用程序中,我使用此实现在我的 SAP R/3 系统中调用 BAPI 来检索数据。
我现在正在开发第二个 Spring MVC tomcat 应用程序,我想在我的 SAP R/3 系统中调用 BAPI 来检索数据。我将调用不同的 BAPI,因此我将检索不同的数据。因为这是调用不同 BAPI 的不同应用程序,所以我想在我的配置中使用不同的 SAP 系统用户。这个新应用程序将 运行 在与第一个应用程序相同的物理 tomcat 服务器上。
我的问题是我应该为这个新应用程序开发另一个 SAP JCO CustomDestinationProvider
实现,还是应该以某种方式重用第一个实现?如果答案是我应该为这个新应用程序开发另一个实现,那么我希望我会为我开发的每个需要与 SAP 通信的新 Spring MVC tomcat 应用程序开发另一个实现。这是正确的想法吗?
如果我为我的这个新应用程序做不同的实现,我应该在代码中使用相同的目标名称,还是应该使用不同的名称?
下面是我第一次实现 CustomDestinationDataProvider
:
的代码
public class CustomDestinationDataProvider {
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) {
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 ArrayList<String> executeSAPCall(Properties connectProperties, ArrayList<String> partnumbers) throws Exception {
String destName = "ABAP_AS";
SAPDAO sapDAO = new SAPDAO();
ArrayList<MaterialBean> searchResults = new ArrayList<MaterialBean>();
MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
JCoDestination dest;
try {
if (!destinationDataProviderRegistered) {
com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
myProvider.changeProperties(destName, connectProperties);
}
} catch(IllegalStateException providerAlreadyRegisteredException) {
logger.error("executeSAPCall: providerAlreadyRegisteredException!");
}
try {
dest = JCoDestinationManager.getDestination(destName);
searchResults = sapDAO.searchSAP(dest, partnumbers);
} catch(JCoException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return searchResults;
}
}
如果答案是我不需要为我的第二个应用程序实施另一个 CustomDestinationDataProvider,我还需要牢记哪些其他注意事项?
您只能注册一个 DestinationDataProvider
因此您设置的必须能够处理两个(或多个)不同的连接。为此,您需要为每个连接起一个唯一的名称,即 destName
不能是固定值 ABAP_AS
,您需要为每个连接创建一个。
你当前的提供者实现对我来说看起来不错,但是你调用 RFC 的方法在我看来混合了连接的创建和实际的 RFC 调用。恕我直言,您应该将前者分离到它自己的方法中,这样您就可以从应用程序的其他部分调用它,例如做 RFC 调用以外的事情。
我想通了!我发现了两种不同的方式来实现 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 应用程序。
今年早些时候,我为我的一个 Spring MVC tomcat 应用程序开发了 SAP JCO CustomDestinationProvider
的实现。在我的应用程序中,我使用此实现在我的 SAP R/3 系统中调用 BAPI 来检索数据。
我现在正在开发第二个 Spring MVC tomcat 应用程序,我想在我的 SAP R/3 系统中调用 BAPI 来检索数据。我将调用不同的 BAPI,因此我将检索不同的数据。因为这是调用不同 BAPI 的不同应用程序,所以我想在我的配置中使用不同的 SAP 系统用户。这个新应用程序将 运行 在与第一个应用程序相同的物理 tomcat 服务器上。
我的问题是我应该为这个新应用程序开发另一个 SAP JCO CustomDestinationProvider
实现,还是应该以某种方式重用第一个实现?如果答案是我应该为这个新应用程序开发另一个实现,那么我希望我会为我开发的每个需要与 SAP 通信的新 Spring MVC tomcat 应用程序开发另一个实现。这是正确的想法吗?
如果我为我的这个新应用程序做不同的实现,我应该在代码中使用相同的目标名称,还是应该使用不同的名称?
下面是我第一次实现 CustomDestinationDataProvider
:
public class CustomDestinationDataProvider {
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) {
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 ArrayList<String> executeSAPCall(Properties connectProperties, ArrayList<String> partnumbers) throws Exception {
String destName = "ABAP_AS";
SAPDAO sapDAO = new SAPDAO();
ArrayList<MaterialBean> searchResults = new ArrayList<MaterialBean>();
MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
boolean destinationDataProviderRegistered = com.sap.conn.jco.ext.Environment.isDestinationDataProviderRegistered();
JCoDestination dest;
try {
if (!destinationDataProviderRegistered) {
com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
myProvider.changeProperties(destName, connectProperties);
}
} catch(IllegalStateException providerAlreadyRegisteredException) {
logger.error("executeSAPCall: providerAlreadyRegisteredException!");
}
try {
dest = JCoDestinationManager.getDestination(destName);
searchResults = sapDAO.searchSAP(dest, partnumbers);
} catch(JCoException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return searchResults;
}
}
如果答案是我不需要为我的第二个应用程序实施另一个 CustomDestinationDataProvider,我还需要牢记哪些其他注意事项?
您只能注册一个 DestinationDataProvider
因此您设置的必须能够处理两个(或多个)不同的连接。为此,您需要为每个连接起一个唯一的名称,即 destName
不能是固定值 ABAP_AS
,您需要为每个连接创建一个。
你当前的提供者实现对我来说看起来不错,但是你调用 RFC 的方法在我看来混合了连接的创建和实际的 RFC 调用。恕我直言,您应该将前者分离到它自己的方法中,这样您就可以从应用程序的其他部分调用它,例如做 RFC 调用以外的事情。
我想通了!我发现了两种不同的方式来实现 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 应用程序。