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