Два веб-приложения, подключающиеся к SAP BAPI с помощью sapjco

У меня есть два веб-приложения, развернутые на WebSphere Application Server 8.5 для RHEL6 (x64). Оба веб-приложения используют библиотеку SAPJCO для подключения к BAPI, развернутому в SAP ECC6. Оба приложения должны иметь доступ к одному и тому же BAPI для создания запросов на покупку. Таким образом, для подключения мы используем тот же код, измененный из SAP CustomDestinationDataProvider. Чтобы гарантировать, что приложение зарегистрирует его только один раз, мы используем Spring, чтобы сделать его одноэлементным.

Однако, когда приложения запускаются, одно приложение (которое, как мы предполагаем, загружается после первого) сталкивается с "java.lang.IllegalStateException: DestinationDataProvider already registered" при выполнении "com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider)".

The CustomDestinationDataProvider is as follow:


    public class CustomDestinationDataProvider {
    public CustomDestinationDataProvider () {
    }

public static BapiConfigBean bapiConfigBean;

//The custom destination data provider implements DestinationDataProvider and
//provides an implementation for at least getDestinationProperties(String).
//Whenever possible the implementation should support events and notify the JCo runtime
//if a destination is being created, changed, or deleted. Otherwise JCo runtime
//will check regularly if a cached destination configuration is still valid which incurs
//a performance penalty.
public static class MyDestinationDataProvider implements DestinationDataProvider
{
    private DestinationDataEventListener eL;
    private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();

    public MyDestinationDataProvider () {

    }
    public Properties getDestinationProperties(String destinationName)
    {
        try
        {
            //read the destination from DB
            Properties p = secureDBStorage.get(destinationName);

            if(p!=null)
            {
                //check if all is correct, for example
                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);
        }
    }

    //An implementation supporting events has to retain the eventListener instance provided
    //by the JCo runtime. This listener instance shall be used to notify the JCo runtime
    //about all changes in destination configurations.
    public void setDestinationDataEventListener(DestinationDataEventListener eventListener)
    {
        this.eL = eventListener;
    }

    public boolean supportsEvents()
    {
        return true;
    }

    //implementation that saves the properties in a very secure way
    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 void removeDestination(String destName) {
        // TODO Auto-generated method stub

    }
    public void addDestination(String destName,
            MyDestinationDataProvider myProvider) {
        // TODO Auto-generated method stub

    }

} // end of MyDestinationDataProvider


//business logic
void executeCalls(String destName)
{
    JCoDestination dest;
    try
    {
        dest = JCoDestinationManager.getDestination(destName);
        dest.ping();
        System.out.println("Destination " + destName + " works");
    }
    catch(JCoException e)
    {
        e.printStackTrace();
        System.out.println("Execution on destination " + destName+ " failed");
    }
}

static Properties getDestinationPropertiesFromUI()
{
    //adapt parameters in order to configure a valid destination
    Properties connectProperties = new Properties();
    connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, getBapiConfigBean().getServerIp());
    connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR,  getBapiConfigBean().getSystemNumber());
    connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, getBapiConfigBean().getClientId());
    connectProperties.setProperty(DestinationDataProvider.JCO_USER,   getBapiConfigBean().getUserName());
    connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, getBapiConfigBean().getUserPassword());
    connectProperties.setProperty(DestinationDataProvider.JCO_LANG,   getBapiConfigBean().getClientLang());
    return connectProperties;
}

static MyDestinationDataProvider myProvider = null; //2014-06-30 00:30 

public static void initProvider(BapiConfigBean bapiConfigBean) {
    CustomDestinationDataProvider.destroy();
    setBapiConfigBean(bapiConfigBean);
    myProvider = new MyDestinationDataProvider();
    String destName = getBapiConfigBean().getSapDestname();

    try
    {

        com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
        CustomDestinationDataProvider test = new CustomDestinationDataProvider();
        myProvider.changeProperties(destName, getDestinationPropertiesFromUI());
        test.executeCalls(destName);

    }
    catch(IllegalStateException providerAlreadyRegisteredException)
    {
        try {
            JCoDestination jcodest = JCoDestinationManager.getDestination(getBapiConfigBean().getSapDestname());
        } catch (JCoException exJCo) {
            //TODO: Add exception handling and send friendly message to ui
        }
    }   
}

public static BapiConfigBean getBapiConfigBean() {
    return bapiConfigBean;
}

public static void setBapiConfigBean(BapiConfigBean bapiConfigBean) {
    CustomDestinationDataProvider.bapiConfigBean = bapiConfigBean;
}

public static void destroy() {
    try {
        Environment.unregisterDestinationDataProvider(myProvider);  
        System.out.println("Unregistered connection to SAP");
    } catch (IllegalStateException e) {  
    System.out.println("Failed to unregister connection to SAP: "+ e);
    }
}
}
</code></pre>

And the error from SystemOut.log in WebSphere is as follow: [6/30/14 22:29:15:198 ICT] 00000043 webapp E com.ibm.ws.webcontainer.webapp.WebApp notifyServletContextCreated SRVE0283E: Exception caught while initializing context: {0} org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jdoConnector' defined in ServletContext resource [/WEB-INF/applicationContext-BAPI.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.sps.tmps.bean.bapi.JCOConnector]: Constructor threw exception; nested exception is java.lang.Error: java.lang.IllegalStateException: DestinationDataProvider already registered [com.sps.tmps.bean.bapi.CustomDestinationDataProvider$MyDestinationDataProvider] Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.sps.tmps.bean.bapi.JCOConnector]: Constructor threw exception; nested exception is java.lang.Error: java.lang.IllegalStateException: DestinationDataProvider already registered [com.sps.tmps.bean.bapi.CustomDestinationDataProvider$MyDestinationDataProvider] Caused by: java.lang.Error: java.lang.IllegalStateException: DestinationDataProvider already registered [com.sps.tmps.bean.bapi.CustomDestinationDataProvider$MyDestinationDataProvider] at com.sps.tmps.bean.bapi.CustomDestinationDataProvider.initProvider(CustomDestinationDataProvider.java:174) at com.sps.tmps.bean.bapi.JCOConnector.(JCOConnector.java:41) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:56) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:39) at java.lang.reflect.Constructor.newInstance(Constructor.java:527) at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:85) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:186) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:800) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:720) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:387) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:251) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:156) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:160) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:287) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:352) at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:244) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:187) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:49) at com.ibm.ws.webcontainer.webapp.WebApp.notifyServletContextCreated(WebApp.java:1678) at com.ibm.ws.webcontainer.webapp.WebAppImpl.initialize(WebAppImpl.java:414) at com.ibm.ws.webcontainer.webapp.WebGroupImpl.addWebApplication(WebGroupImpl.java:88) at com.ibm.ws.webcontainer.VirtualHostImpl.addWebApplication(VirtualHostImpl.java:169) at com.ibm.ws.webcontainer.WSWebContainer.addWebApp(WSWebContainer.java:749) at com.ibm.ws.webcontainer.WSWebContainer.addWebApplication(WSWebContainer.java:634) at com.ibm.ws.webcontainer.component.WebContainerImpl.install(WebContainerImpl.java:426) at com.ibm.ws.webcontainer.component.WebContainerImpl.start(WebContainerImpl.java:718) at com.ibm.ws.runtime.component.ApplicationMgrImpl.start(ApplicationMgrImpl.java:1175) at com.ibm.ws.runtime.component.DeployedApplicationImpl.fireDeployedObjectStart(DeployedApplicationImpl.java:1370) at com.ibm.ws.runtime.component.DeployedModuleImpl.start(DeployedModuleImpl.java:639) at com.ibm.ws.runtime.component.DeployedApplicationImpl.start(DeployedApplicationImpl.java:968) at com.ibm.ws.runtime.component.ApplicationMgrImpl.startApplication(ApplicationMgrImpl.java:774) at com.ibm.ws.runtime.component.ApplicationMgrImpl.start(ApplicationMgrImpl.java:2182) at com.ibm.ws.runtime.component.CompositionUnitMgrImpl.start(CompositionUnitMgrImpl.java:445) at com.ibm.ws.runtime.component.CompositionUnitImpl.start(CompositionUnitImpl.java:123) at com.ibm.ws.runtime.component.CompositionUnitMgrImpl.start(CompositionUnitMgrImpl.java:388) at com.ibm.ws.runtime.component.CompositionUnitMgrImpl.access$500(CompositionUnitMgrImpl.java:116) at com.ibm.ws.runtime.component.CompositionUnitMgrImpl$CUInitializer.run(CompositionUnitMgrImpl.java:994) at com.ibm.wsspi.runtime.component.WsComponentImpl$_AsynchInitializer.run(WsComponentImpl.java:502) at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1862) Caused by: java.lang.IllegalStateException: DestinationDataProvider already registered [com.sps.tmps.bean.bapi.CustomDestinationDataProvider$MyDestinationDataProvider] at com.sap.conn.jco.rt.RuntimeEnvironment.setDestinationDataProvider(RuntimeEnvironment.java:134) at com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(Environment.java:259) at com.sps.tmps.bean.bapi.CustomDestinationDataProvider.initProvider(CustomDestinationDataProvider.java:156) ... 41 more

Мои вопросы:
1) Как сделать так, чтобы два приложения могли использовать один и тот же CustomDestinationDataProvider?
2) Если 2 приложения не могут использовать одного и того же провайдера, как я могу получить доступ к одному и тому же BAPI, используя один и тот же номер сервера/пользователя/клиента?
Ограничения в этом случае таковы: (1) мы должны использовать sapjco и (2) у нас есть только функция BAPI. customDestinationDataProvider можно отбросить, если вы считаете, что это проблема. Кстати, я извинился за длинный код и ужасное форматирование.
спасибо.


person wittaya    schedule 03.07.2014    source источник


Ответы (1)


Простое решение вашей проблемы - использовать разные имена назначения для двух ваших приложений (хотя в вашем случае они одинаковы). Используйте два разных имени назначения, по одному для каждого приложения. например PROD_APP1, PROD_APP2

Все остальное может остаться прежним, я считаю.

В идеальной ситуации я бы предпочел более надежное решение. Может быть, я думаю о строках пользовательского загрузчика классов и т. Д.

Удачи!

person Gana    schedule 04.07.2014
comment
Спасибо. У нас тоже есть тест, и он решает часть нашей проблемы. Хотя ошибка по-прежнему появляется, когда я выбираю обновить приложение вместо удаления и установки приложения в WebSphere. Я предполагаю, что пример кода, предоставленный SAP JCO для пользовательского поставщика, должен быть смешан и сопоставлен с многопоточным примером, чтобы сделать пригодный для использования код. - person wittaya; 14.07.2014