4

I've a problem with GlassFish and SAP JCo connector (sapjco3.jar).

I load it at start up of an J2EE application (jwm.ear) and initialize it wrapped in a singleton the first time a connection to SAP is needed.

The problem is that this jar remains always initialized in memory, I need to restart glassfish to unload the initialized connections if I need to change a single parameter. Stopping or undeploying the app doesn't unload sapjco.jar and furter redeployments of the app never get the new connection parameters, the first initialization remains until GlassFish restart.

Does anybody knows how to unload or reinitialize this library? preferably even without redeploying the app, first time the app is activated I have a reference to jcoProvider, next activations get a null reference to jcoProvider, but a jcoProvider continues instantiated in memory with initial values.

Regards!

Notes: GlassFish is version 2.1 in Windows 2008 server, jdk is 1.6.0.14 sapjco3.jar and sapjco3.dll are copied to \domains\domain1\lib\ext and are version 3 of SAP java connector.

Singleton to get SAP connections:

package es.grupotec.ejb.SAP;

import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.Environment;
import es.grupotec.ejb.util.ConexionSAPException;
import java.util.Properties;

public final class SAP {

    private static String SAP_SERVER = "JWM";
    private static SAP instance = null;
    private static JCOProvider jcoProvider = null;

    private SAP() {
      // Exists only to defeat instantiation.
    }

    // Get SAP connection
    public static synchronized JCoDestination getDestination() throws ConexionSAPException {

        JCoDestination jcoDestination = null;

            if (Environment.isDestinationDataProviderRegistered()) {
                try {

                    jcoDestination = JCoDestinationManager.getDestination(SAP_SERVER);
                    return jcoDestination;

                } catch (JCoException ex) {

                    throw new ConexionSAPException(ex.getMessage());

                }
            }

        // Create new connection
        if(jcoProvider == null) init();

        // Get connection
        try {

            jcoDestination = JCoDestinationManager.getDestination(SAP_SERVER);
            return jcoDestination;
            
        } catch (JCoException ex) {
        
            throw new ConexionSAPException(ex.getMessage());

        }

    }

    // Initialize connection to SAP
    public static synchronized void init() throws ConexionSAPException {

        SAPVO sap = new SAPVO();
        Properties properties = new Properties();

        if(jcoProvider == null) {


            // Get SAP config from database
            try {
                sap = SAPDAO.getSAPConfig();
            } catch (Exception ex) {
                throw new ConexionSAPException(ex.getMessage());
            }

             // Create connection object
            jcoProvider = new JCOProvider();

        }

        properties.setProperty(DestinationDataProvider.JCO_ASHOST,        sap.getJCO_ASHOST());
        properties.setProperty(DestinationDataProvider.JCO_SYSNR,         sap.getJCO_SYSNR());
        properties.setProperty(DestinationDataProvider.JCO_CLIENT,        sap.getJCO_CLIENT());
        properties.setProperty(DestinationDataProvider.JCO_USER,          sap.getJCO_USER());
        properties.setProperty(DestinationDataProvider.JCO_PASSWD,        sap.getJCO_PASSWD());
        properties.setProperty(DestinationDataProvider.JCO_LANG,          sap.getJCO_LANG());

        try {

            jcoProvider.changePropertiesForABAP_AS(properties);

        } catch (Exception e) {

            throw new ConexionSAPException(e.getMessage());

        }

    }

    public static synchronized void change(SAPVO sap) throws ConexionSAPException {

        Properties properties = new Properties();

        // If connection is null create a new one
        if(jcoProvider == null) jcoProvider = new JCOProvider();

        properties.setProperty(DestinationDataProvider.JCO_ASHOST,        sap.getJCO_ASHOST());
        properties.setProperty(DestinationDataProvider.JCO_SYSNR,         sap.getJCO_SYSNR());
        properties.setProperty(DestinationDataProvider.JCO_CLIENT,        sap.getJCO_CLIENT());
        properties.setProperty(DestinationDataProvider.JCO_USER,          sap.getJCO_USER());
        properties.setProperty(DestinationDataProvider.JCO_PASSWD,        sap.getJCO_PASSWD());
        properties.setProperty(DestinationDataProvider.JCO_LANG,          sap.getJCO_LANG());

        try {

            jcoProvider.changePropertiesForABAP_AS(properties);

        } catch (Exception e) {

            throw new ConexionSAPException(e.getMessage());

        }


    }

    // Prevent instantiation by clone
    @Override
    public Object clone() throws CloneNotSupportedException {
        
        throw new CloneNotSupportedException();

    }

}

JCo provider implementation:

package es.grupotec.ejb.SAP;

import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.Environment;
import es.grupotec.ejb.util.ConexionSAPException;
import java.util.Properties;

public class JCOProvider implements DestinationDataProvider {

    private String SAP_SERVER = "JWM";
    private DestinationDataEventListener eventListener;
    private Properties ABAP_AS_properties;

    public JCOProvider(){

    }
    
    public JCOProvider(SAPVO sap){

        ABAP_AS_properties = new Properties();
        ABAP_AS_properties.setProperty(DestinationDataProvider.JCO_ASHOST,        sap.getJCO_ASHOST());
        ABAP_AS_properties.setProperty(DestinationDataProvider.JCO_SYSNR,         sap.getJCO_SYSNR());
        ABAP_AS_properties.setProperty(DestinationDataProvider.JCO_CLIENT,        sap.getJCO_CLIENT());
        ABAP_AS_properties.setProperty(DestinationDataProvider.JCO_USER,          sap.getJCO_USER());
        ABAP_AS_properties.setProperty(DestinationDataProvider.JCO_PASSWD,        sap.getJCO_PASSWD());
        ABAP_AS_properties.setProperty(DestinationDataProvider.JCO_LANG,          sap.getJCO_LANG());
        ABAP_AS_properties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, sap.getJCO_POOL_CAPACITY());
        ABAP_AS_properties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT,    sap.getJCO_PEAK_LIMIT());

        try {
            if (!Environment.isDestinationDataProviderRegistered())
                Environment.registerDestinationDataProvider(this);
            else changePropertiesForABAP_AS(ABAP_AS_properties);
        } catch (Exception ex) {
            String msg = ex.getMessage();
        }

    }

    @Override
    public Properties getDestinationProperties(String name) {

        if (name.equals(SAP_SERVER) && ABAP_AS_properties!=null) return ABAP_AS_properties;
        else return null;
        
    }

    @Override
    public boolean supportsEvents() {
        return true;
    }

    @Override
    public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
        this.eventListener = eventListener;
    }

 public void changePropertiesForABAP_AS(Properties properties) throws ConexionSAPException {

        try {
            
            if (!Environment.isDestinationDataProviderRegistered()) {

                if (ABAP_AS_properties == null) ABAP_AS_properties = properties;
                Environment.registerDestinationDataProvider(this);

            }

            if (properties == null) {

                if (eventListener != null) eventListener.deleted(SAP_SERVER);
                ABAP_AS_properties = null;

            } else {

                ABAP_AS_properties = properties;
                if (eventListener != null) eventListener.updated(SAP_SERVER);

            }

        } catch (Exception ex) {

            throw new ConexionSAPException(ex.getMessage());
            
        }

 }
    
}

Sandra Rossi
  • 11,934
  • 5
  • 22
  • 48
franblay
  • 306
  • 3
  • 8

2 Answers2

3

Your problem is probably related to the fact that there is some native code involved here. This is even true for JCo 3. Whereas JCo 3 does not make usage of the native RFC library anymore, it still requires JNI to communicate with the CPIC layer.

Getting a JVM to unload a native library is an exercise in utmost frustration. The JNI Specification states that a native library will be unloaded when the ClassLoader associated with the class it provides the implementation to is unloaded, but trying to force a ClassLoader to unload is virtually impossible within the JVM.

If your EAR file includes the sapjco3.jar it will be reloaded each time your code is reloaded. This very likely will causes exceptions as the native library can't be loaded more than once and there is practically no way to unload native code. So you might consider to place the sapjco3.jar outside of the J2EE container and let your J2EE engine load that library once at start time rather than putting it into the EAR that gets reloaded over and over.

Tom
  • 424
  • 4
  • 9
  • Yes, I've fight first with this issue (included in .ear), now sapjco3.jar and sapjco3.dll are placed in domains/domain1/lib/ext and works fine, the only issue is in case of SAP connection changes, therefore they aren't common, it's only necessary to restart glassfish when SAP forces a password change in the user we are connecting with. I thought in classloader way but this is black magic to me. Thanks a lot. – franblay Dec 28 '09 at 10:56
  • Even after placing sapjco3.jar under the lib/ext path, you should still be able to create, delete, change your JCo destination and provider objects as part of your container. Therefore I don't see any reason why one should not be able to alter a JCo connection object by changing any of the connection properties like its password. – Tom Dec 29 '09 at 00:22
  • Yes, yes, we can do a changePropertiesForABAP_AS whenever a change in the connection occurs... but as far as you have a "fresh" deployment of the app. If we do a redeploy, or deactivation/activation, the link between our app. and jco connection object gets broken. We can use the connection, as it is somehow alive but we can't change it. jcoDestination=JCoDestinationManager.getDestination(); works always, but after an update of the app changePropertiesForABAP_AS fails, object eventListener=NULL. We need to restart the server. Maybe I missed some point in the internals of the jco connector.... – franblay Jan 07 '10 at 08:41
0

Which release of SAP are you intending to connect to? We had several problems with the Java Connector, it was not really threadsafe and could not be embedded into a EJB application properly. Same problems came with SAP's seculib for single sign on. It either didn't work. The only solution was to load it outside of the J2EE engine.

Have you ever thought of replacing the JCO with webservices? Of course it's slightly slower as the data has to go through the ICF but it's more robust. We switched all our integrations to this solution.

  • The sap connector works very well, the only issue is what i asked for and we can live with it, it's a "ugly" solution but we can afford a restart unpon a single connection change (user password mostly) I thought about web services but this is a very intensive app. with sinchronizations and BAPI executions every 5 sec. or so and sometimes big amounts of data. WS are more "asincronous" and we needed this feeling of "all under control" with RFC. But it's a way to have in mind. Thanks a lot. – franblay Dec 28 '09 at 11:03