wittaya
wittaya

Reputation: 25

Two web applications connecting to SAP BAPI using SAP JCo

I have two web applications deployed on WebSphere Application Server 8.5 for RHEL6 (x64). Both web applications use SAP JCo library to connect to BAPI deployed in SAP ECC6. Both of the application need to access the same BAPI in order to create Purchase Requests. So, we use the same code modified from SAP's CustomDestinationDataProvider to connect. To ensure that application register it only once, we use Spring to make it a singleton.

However, when applications started, one application (which we guess it is loaded after the first) encounters this error when com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider) is executed

java.lang.IllegalStateException: DestinationDataProvider already registered

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

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.<init>(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

My questions are:

  1. How to make two application able to use the same CustomDestinationDataProvider?
  2. If 2 applications cannot use the same provider, how can I access the same BAPI using the same server/user/client number?

Constraints in this case are that (1) We should use SAP JCo and (2) we have only one BAPI function. customDestinationDataProvider can be discarded if you think it is a problem.

By the way, I apologized for the long code and terrible formatting.

Thank you.

Upvotes: 1

Views: 4094

Answers (1)

Gana
Gana

Reputation: 492

A simple solution for your problem is to use different destination Names for your two applications (though they are same in your case). Use two different destination names, one for each application. e.g. PROD_APP1, PROD_APP2

All other things can remain same, i believe.

In an ideal situation, i would prefer a more robust solution. May be i think in the lines of a custom class loader, etc

Good Luck!

Upvotes: 2

Related Questions