【问题标题】:SAP JCo connector loaded forever in GlassFish v2.1 (can't unload)在 GlassFish v2.1 中永久加载的 SAP JCo 连接器(无法卸载)
【发布时间】:2010-12-26 08:57:17
【问题描述】:

GlassFish 和 SAP JCo 连接器 (sapjco3.jar) 有问题

我在启动 J2EE 应用程序 (jwm.ear) 时加载它,并在第一次需要连接到 SAP 时将其初始化为单例。

问题是这个 jar 始终在内存中初始化,如果我需要更改单个参数,我需要重新启动 glassfish 以卸载初始化的连接。停止或取消部署应用程序不会卸载 sapjco.jar,并且应用程序的进一步重新部署永远不会获得新的连接参数,第一次初始化将一直持续到 GlassFish 重新启动。

有人知道如何卸载或重新初始化这个库吗?最好即使没有重新部署应用程序,第一次激活应用程序时我有对 jcoProvider 的引用,下一次激活会获得对 jcoProvider 的空引用,但 jcoProvider 会继续在内存中使用初始值实例化。

问候!

注意事项: GlassFish 是 Windows 2008 服务器中的 2.1 版本,jdk 是 1.6.0.14 sapjco3.jar 和 sapjco3.dll 被复制到 \domains\domain1\lib\ext 并且是 SAP java 连接器的版本 3。

单例获取 SAP 连接:

包 es.grupotec.ejb.SAP; 导入 com.sap.conn.jco.JCoDestination; 导入 com.sap.conn.jco.JCoDestinationManager; 导入 com.sap.conn.jco.JCoException; 导入 com.sap.conn.jco.ext.DestinationDataProvider; 导入 com.sap.conn.jco.ext.Environment; 导入 es.grupotec.ejb.util.ConexionSAPException; 导入 java.util.Properties; 公共最终类 SAP { 私有静态字符串 SAP_SERVER = "JWM"; 私有静态 SAP 实例 = null; 私有静态 JCOProvider jcoProvider = null; 私人 SAP() { // 仅用于阻止实例化。 } // 获取 SAP 连接 公共静态同步 JCoDestination getDestination() 抛出 ConexionSAPException { JCoDestination jcoDestination = null; if (Environment.isDestinationDataProviderRegistered()) { 尝试 { jcoDestination = JCoDestinationManager.getDestination(SAP_SERVER); 返回 jcoDestination; } 捕捉(JCoException ex){ 抛出新的 ConexionSAPException(ex.getMessage()); } } // 创建新连接 如果(jcoProvider == null)初始化(); // 获取连接 尝试 { jcoDestination = JCoDestinationManager.getDestination(SAP_SERVER); 返回 jcoDestination; } 捕捉(JCoException ex){ 抛出新的 ConexionSAPException(ex.getMessage()); } } // 初始化与 SAP 的连接 公共静态同步 void init() 抛出 ConexionSAPException { SAPVO sap = 新 SAPVO(); 属性属性 = 新属性(); 如果(jcoProvider == null){ // 从数据库中获取 SAP 配置 尝试 { sap = SAPDAO.getSAPConfig(); } 捕捉(异常前){ 抛出新的 ConexionSAPException(ex.getMessage()); } // 创建连接对象 jcoProvider = 新的 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()); 尝试 { jcoProvider.changePropertiesForABAP_AS(属性); } 捕捉(异常 e){ 抛出新的 ConexionSAPException(e.getMessage()); } } 公共静态同步无效更改(SAPVO sap)抛出 ConexionSAPException { 属性属性 = 新属性(); // 如果连接为空,则创建一个新的 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()); 尝试 { jcoProvider.changePropertiesForABAP_AS(属性); } 捕捉(异常 e){ 抛出新的 ConexionSAPException(e.getMessage()); } } // 防止通过克隆实例化 @覆盖 公共对象克隆()抛出 CloneNotSupportedException { 抛出新的 CloneNotSupportedException(); } }

JCo 提供者实现:

包 es.grupotec.ejb.SAP; 导入 com.sap.conn.jco.ext.DestinationDataEventListener; 导入 com.sap.conn.jco.ext.DestinationDataProvider; 导入 com.sap.conn.jco.ext.Environment; 导入 es.grupotec.ejb.util.ConexionSAPException; 导入 java.util.Properties; 公共类 JCOProvider 实现 DestinationDataProvider { 私有字符串 SAP_SERVER = "JWM"; 私有 DestinationDataEventListener 事件监听器; 私有属性 ABAP_AS_properties; 公共 JCOProvider(){ } 公共 JCOProvider(SAPVO 树液){ ABAP_AS_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()); 尝试 { if (!Environment.isDestinationDataProviderRegistered()) Environment.registerDestinationDataProvider(this); 否则 changePropertiesForABAP_AS(ABAP_AS_properties); } 捕捉(异常前){ 字符串味精 = ex.getMessage(); } } @覆盖 公共属性 getDestinationProperties(字符串名称){ if (name.equals(SAP_SERVER) && ABAP_AS_properties!=null) 返回 ABAP_AS_properties; 否则返回空值; } @覆盖 公共布尔支持事件(){ 返回真; } @覆盖 公共无效 setDestinationDataEventListener(DestinationDataEventListener eventListener) { this.eventListener = 事件监听器; } 公共无效changePropertiesForABAP_AS(属性属性)抛出ConexionSAPException { 尝试 { if (!Environment.isDestinationDataProviderRegistered()) { 如果(ABAP_AS_properties == null)ABAP_AS_properties = 属性; Environment.registerDestinationDataProvider(this); } 如果(属性 == null){ if (eventListener != null) eventListener.deleted(SAP_SERVER); ABAP_AS_properties = null; } 别的 { ABAP_AS_properties = 属性; if (eventListener != null) eventListener.updated(SAP_SERVER); } } 捕捉(异常前){ 抛出新的 ConexionSAPException(ex.getMessage()); } } }

【问题讨论】:

    标签: java jakarta-ee glassfish sap


    【解决方案1】:

    您的问题可能与此处涉及一些本机代码有关。 JCo 3 也是如此。虽然 JCo 3 不再使用原生 RFC 库,但它仍然需要 JNI 与 CPIC 层进行通信。

    让 JVM 卸载本机库是一项非常令人沮丧的练习。 JNI 规范规定,当与它提供实现的类关联的 ClassLoader 被卸载时,本地库将被卸载,但在 JVM 中试图强制 ClassLoader 卸载几乎是不可能的。

    如果您的 EAR 文件包含 sapjco3.jar,则每次重新加载代码时都会重新加载它。这很可能会导致异常,因为本机库不能多次加载,并且实际上没有办法卸载本机代码。因此,您可能会考虑将 sapjco3.jar 放在 J2EE 容器之外,让您的 J2EE 引擎在启动时加载该库一次,而不是将其放入反复重新加载的 EAR。

    【讨论】:

    • 是的,我首先解决了这个问题(包含在 .ear 中),现在 sapjco3.jar 和 sapjco3.dll 被放置在 domains/domain1/lib/ext 中并且工作正常,唯一的问题是在 SAP 连接更改的情况下,因此它们并不常见,只有当 SAP 强制我们正在连接的用户更改密码时,才需要重新启动 glassfish。我以类加载器的方式思考,但这对我来说是黑魔法。非常感谢。
    • 即使将 sapjco3.jar 放在 lib/ext 路径下,您仍然应该能够创建、删除、更改 JCo 目标和提供者对象作为容器的一部分。因此,我看不出有什么理由不能通过更改任何连接属性(如密码)来更改 JCo 连接对象。
    • 是的,是的,只要连接发生变化,我们就可以执行 changePropertiesForABAP_AS ......但只要您有一个“全新”的应用程序部署。如果我们重新部署或停用/激活,我们的应用程序之间的链接。并且 jco 连接对象被破坏。我们可以使用连接,因为它以某种方式存在,但我们无法更改它。 jcoDestination=JCoDestinationManager.getDestination();始终有效,但在更新应用程序 changePropertiesForABAP_AS 失败后,对象 eventListener=NULL。我们需要重新启动服务器。也许我错过了 jco 连接器内部的一些要点......
    【解决方案2】:

    您打算连接到哪个版本的 SAP? Java 连接器有几个问题,它不是真正的线程安全的,不能正确嵌入到 EJB 应用程序中。 SAP 的单点登录 seculib 也出现了同样的问题。它要么没有工作。唯一的解决方案是在 J2EE 引擎之外加载它。

    您有没有想过用 Web 服务代替 JCO?当然,它会稍微慢一些,因为数据必须通过 ICF,但它更健壮。我们将所有集成都切换到此解决方案。

    【讨论】:

    • sap 连接器工作得很好,唯一的问题是我要求的,我们可以接受它,这是一个“丑陋”的解决方案,但我们可以在单个连接更改后重新启动(用户密码大多数情况下)我考虑过网络服务,但这是一个非常密集的应用程序。每 5 秒进行一次同步和 BAPI 执行。左右,有时是大量数据。 WS 更“愚蠢”,我们需要 RFC 这种“一切尽在掌控”的感觉。但这是一种要记住的方法。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2014-10-26
    • 2019-07-14
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 2016-04-18
    • 1970-01-01
    • 2023-01-28
    相关资源
    最近更新 更多