在 Tomcat 服务器上的应用程序中实现不同的目的地 [英] Implementing different destinations in applications on the Tomcat server

查看:31
本文介绍了在 Tomcat 服务器上的应用程序中实现不同的目的地的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今年早些时候,我为我的一个 Spring MVC tomcat 应用程序开发了 SAP JCO CustomDestinationProvider 的实现.在我的应用程序中,我使用此实现调用 SAP R/3 系统中的 BAPI 来检索数据.

Earlier this year I developed an implementation of the SAP JCO CustomDestinationProvider for one of my Spring MVC tomcat applications. In my application, I use this implementation to call a BAPI in my SAP R/3 system to retrieve data.

我现在正在开发第二个 Spring MVC tomcat 应用程序,我希望在我的 SAP R/3 系统中调用 BAPI 来检索数据.我将调用不同的 BAPI,因此我将检索不同的数据.由于这是一个调用不同 BAPI 的不同应用程序,我想在我的配置中使用不同的 SAP 系统用户.这个新应用程序将在与第一个应用程序相同的物理 tomcat 服务器上运行.

I am now working on a second Spring MVC tomcat application that I want to have call a BAPI in my SAP R/3 system to retrieve data. It will be a different BAPI that I will be calling, thus it will be different data that I will be retrieving. Since this is a different application calling a different BAPI, I want to use a different SAP system user in my configurations. This new application will be running on the same physical tomcat server as the first application.

我的问题是我应该为此新应用程序开发 SAP JCO CustomDestinationProvider 的另一个实现,还是应该以某种方式重用第一个实现?如果答案是我应该为这个新应用程序开发另一个实现,那么我希望我会为我开发的每个需要与 SAP 对话的新 Spring MVC tomcat 应用程序开发另一个实现.这是正确的想法吗?

My question is should I develop another implementation of the SAP JCO CustomDestinationProvider for this new application or should I somehow reuse the first implementation? If the answer is that I should develop another implementation for this new application, I would expect then that I would develop another implementation for each new Spring MVC tomcat application that I develop that needs to talk to SAP. Is this correct thinking?

如果我为我的这个新应用程序执行不同的实现,我应该在代码中使用相同的目标名称,还是应该使用不同的名称?下面是我第一次实现 CustomDestinationDataProvider 的代码:

If I do a different implementation for this new application of mine, should I be using the same destination name in the code, or should I use a different name? Below is the code for my first implementation of 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,我还需要记住哪些其他注意事项?

If the answer is that I should not need to implement another CustomDestinationDataProvider for my second application, what other considerations do I need to keep in mind?

推荐答案

我想通了!我发现了两种不同的方法来实现 CustomDestinationDataProvider,以便我可以使用多个目的地.

I've figured it out! I discovered two different ways to implement CustomDestinationDataProvider so that I could use multiple destinations.

我所做的对我的两种不同解决方案都有帮助的事情是更改 CustomDestinationDataProvider 中的方法,该方法实例化 MyDestinationDataProvider 内部类,以便它不返回 ArrayList,而是返回 JCoDestination.我将此方法的名称从 executeSAPCall 更改为 getDestination.

Something that I did that helped out with both of my different solutions was change out the method in CustomDestinationDataProvider that instantiates the MyDestinationDataProvider inner class so that instead of returning ArrayList, it returns JCoDestination. I changed the name of this method from executeSAPCall to getDestination.

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

The first way that I discovered that allowed me to use multiple destinations, successfully changing out destinations, was to introduce a class variable for MyDestinationDataProvider so that I could keep my instantiated version. Please note that for this solution, the CustomDestinationDataProvider class is still embedded within my java application code.

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

I found that this solution only worked for one application. I was not able to use this mechanism in multiple applications on the same tomcat server, but at least I was finally able to successfully switch destinations. Here is the code for CustomDestinationDataProvider.java for this first solution:

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 类中的代码,我用来在我的应用程序代码中实例化和调用 CustomDestinationDataProvider:

This is the code in my servlet class that I use to instantiate and call CustomDestinationDataProvider within my application code:

    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();
    }

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

Again, this solution only works within one application. If you implement this code directly into more than one application, the first app that calls this code gets the resource and the other one will error out.

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

The second solution that I came up with allows multiple java applications to use the CustomDestinationDataProvider class at the same time. I broke the CustomDestinationDataProvider class out of my application code and created a separate java spring application for it (not a web application) for the purpose of creating a jar. I then transformed the MyDestinationDataProvider inner class into a singleton. Here's the code for the singleton version of 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 行的共享库类路径的位置.我的看起来像这样:

After putting this code into the jar file application and creating the jar file (I call it JCOConnector.jar), I put the jar file on the shared library classpath of my tomcat server and restarted the tomcat server. In my case, this was /opt/tomcat/shared/lib. Check your /opt/tomcat/conf/catalina.properties file for the shared.loader line for the location of your shared library classpath. Mine looks like this:

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

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

I also put a copy of this jar file in the "C:\Users\userid\Documents\jars" folder on my workstation so that the test application code could see the code in the jar and compile. I then referenced this copy of the jar file in my pom.xml file in both of my test applications:

    <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 的副本直接添加到我的任何一个测试项目中.这样做的原因是因为我希望使用/opt/tomcat/shared/lib/JCOConnector.jar 中的 jar 文件中的代码.然后我构建并部署了我的每个测试应用到 tomcat 服务器.

After adding this to the pom.xml file, I right clicked on each project, selected Maven -> Update Project..., and I then right clicked again on each project and selected 'Refresh'. Something very important that I learned was to not add a copy of JCOConnector.jar directly to either of my test projects. The reason for this is because I want the code from the jar file in /opt/tomcat/shared/lib/JCOConnector.jar to be used. I then built and deployed each of my test apps to the tomcat server.

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

The code that calls my JCOConnector.jar shared library in my first test application looks like this:

    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 共享库的代码如下所示:

The code in my second test application that calls my JCOConnector.jar shared library looks like this:

    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 应用程序的困难.

I know that I've left out a lot of the steps involved in first getting the SAP JCO 3 library installed on your workstation and server. I do hope that this helps out at least one other person of getting over the hill of trying to get multiple spring mvc java spplications talking to SAP on the same server.

这篇关于在 Tomcat 服务器上的应用程序中实现不同的目的地的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆