在 GlassFish v2.1 中永久加载的 SAP JCo 连接器(无法卸载)

Posted

技术标签:

【中文标题】在 GlassFish v2.1 中永久加载的 SAP JCo 连接器(无法卸载)【英文标题】:SAP JCo connector loaded forever in GlassFish v2.1 (can't unload) 【发布时间】: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());

【问题讨论】:

【参考方案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 这种“一切尽在掌控”的感觉。但这是一种要记住的方法。非常感谢。

以上是关于在 GlassFish v2.1 中永久加载的 SAP JCo 连接器(无法卸载)的主要内容,如果未能解决你的问题,请参考以下文章

Glassfish 4.0 加载应用程序时出现异常,java.lang.IllegalStateException

Linux Mint - 永久添加环境变量[关闭]

在GlassFish 3上为Web应用程序禁用CDI

在 Glassfish 服务器 5.0 中部署 ADF1.2.2.1.4 时出现异常

设置 GlassFish 服务器

Java EE/Glassfish 应用程序逻辑