【问题标题】:EJB to JNI (C++) callEJB 到 JNI (C++) 调用
【发布时间】:2014-06-26 13:44:56
【问题描述】:

我正在尝试使用 JNI 从 EJB 调用 windows (dll) 上的本机库。我已经阅读了很多博客和网站建议在 EJB 规范中不允许从 EJB 进行 JNI 调用。但是有供应商特定的例外。我使用 weblogic 作为 EJB 容器。如果你能在这个领域指导/建议我。我知道资源适配器是一种选择,但要实现简单的要求是一项艰巨的任务。这是我的方法。

我开发了一个简单的 JNI,它调用一个 C++ 本地库(也是我构建的)并打印输出:

所以 JNI 类 HelloJNICpp 调用 dll 并调用本地方法 sayHello(),用 C++ 实现,它给出了输出

现在,我实现了一个会话 bean (TestEJBBean.java),一个由 bean 调用的包装类 HelloJNICpp.java(如 JNI)。最后一个独立的java客户端TestEJBClient.java来测试bean,是否可以调用,是否可以调用native。

这里是代码详情:

远程接口

package com.test.services;

import javax.ejb.Remote;

@Remote
public interface TestEJBRemote {
    public  void helloJNI();
}

会话 Bean

package com.test.services;

import javax.ejb.Stateless;

@Stateless(mappedName = "TestEJB")
public class TestEJBBean implements TestEJBRemote {
    public void helloJNI(){
        new HelloJNICpp(). hello();  // Invoke native method
        }
}

包装类用作 JNI

package com.test.services;

public class HelloJNICpp {

static {
      //System.load("hellocpp"); // hello.dll (Windows) or libhello.so (Unixes)
     try{
     System.loadLibrary("hellocpp");
     }catch( Exception e){
         System.out.println("Some problem occurred while loading library.");
     }
   }    

   // Native method declaration
   private native void sayHello();

   // Test Driver
   public static void hello() {
      new HelloJNICpp().sayHello();  // Invoke native method

   }
}

Bean 测试客户端

package com.test.client;

import com.test.services.*;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class TestEJBClient {
public static void main (String args[]){
try {
        Context ctx = new InitialContext(getInitialContext());
        TestEJBRemote testBean = (TestEJBRemote) ctx.lookup("TestEJB#com.test.services.TestEJBRemote");
         testBean.helloJNI();
    } catch (NamingException e) {
        e.printStackTrace();
    }
}

     private static Hashtable<String, Object> getInitialContext() {
        Hashtable<String, Object> properties = new Hashtable<String, Object>();
        properties.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
        properties.put(Context.PROVIDER_URL, "t3://localhost:7001");
        return properties;
    }
}

我将 dll 库放置在所有可能的位置,例如 java 库路径可以包括 C:\Windows\System32 、weblogic 域库,甚至我将它与 EJB 应用程序捆绑并部署它。当我执行客户端时,我得到 UnsatisfiedLink 错误。

Exception in thread "main" javax.ejb.EJBException: EJB Exception: ; nested exception is: 
java.lang.UnsatisfiedLinkError: com.test.services.HelloJNICpp.sayHello()V; nested exception is: java.rmi.RemoteException: EJB Exception: ; nested exception is: 
java.lang.UnsatisfiedLinkError: com.test.services.HelloJNICpp.sayHello()V
java.rmi.RemoteException: EJB Exception: ; nested exception is: 
java.lang.UnsatisfiedLinkError: com.test.services.HelloJNICpp.sayHello()V
at weblogic.rjvm.ResponseImpl.unmarshalReturn(ResponseImpl.java:237)
at weblogic.rmi.cluster.ClusterableRemoteRef.invoke(ClusterableRemoteRef.java:348)
at weblogic.rmi.cluster.ClusterableRemoteRef.invoke(ClusterableRemoteRef.java:259)
at com.test.services.TestEJBBean_vdl7a8_TestEJBRemoteImpl_1036_WLStub.helloJNI(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at weblogic.ejb.container.internal.RemoteBusinessIntfProxy.invoke(RemoteBusinessIntfProxy.java:85)
at $Proxy0.helloJNI(Unknown Source)
at com.test.client.TestEJBClient.main(TestEJBClient.java:14)
Caused by: java.lang.UnsatisfiedLinkError: com.test.services.HelloJNICpp.sayHello()V
at com.test.services.HelloJNICpp.sayHello(Native Method)
at com.test.services.HelloJNICpp.hello(HelloJNICpp.java:19)
at com.test.services.TestEJBBean.helloJNI(TestEJBBean.java:9)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at     sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at      com.bea.core.repackaged.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:310)
at com.bea.core.repackaged.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
at com.bea.core.repackaged.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
at com.bea.core.repackaged.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:131)
at com.bea.core.repackaged.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:119)
at com.bea.core.repackaged.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at com.oracle.pitchfork.spi.MethodInvocationVisitorImpl.visit(MethodInvocationVisitorImpl.java:34)
at weblogic.ejb.container.injection.EnvironmentInterceptorCallbackImpl.callback(EnvironmentInterceptorCallbackImpl.java:54)
at com.oracle.pitchfork.spi.EnvironmentInterceptor.invoke(EnvironmentInterceptor.java:42)
at com.bea.core.repackaged.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at com.bea.core.repackaged.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at com.bea.core.repackaged.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at com.bea.core.repackaged.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:131)
at com.bea.core.repackaged.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:119)
at com.bea.core.repackaged.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at com.bea.core.repackaged.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy200.helloJNI(Unknown Source)
at com.test.services.TestEJBBean_vdl7a8_TestEJBRemoteImpl.__WL_invoke(Unknown Source)
at weblogic.ejb.container.internal.SessionRemoteMethodInvoker.invoke(SessionRemoteMethodInvoker.java:40)
at com.test.services.TestEJBBean_vdl7a8_TestEJBRemoteImpl.helloJNI(Unknown Source)
at com.test.services.TestEJBBean_vdl7a8_TestEJBRemoteImpl_WLSkel.invoke(Unknown Source)
at weblogic.rmi.internal.BasicServerRef.invoke(BasicServerRef.java:667)
at weblogic.rmi.cluster.ClusterableServerRef.invoke(ClusterableServerRef.java:230)
at weblogic.rmi.internal.BasicServerRef$1.run(BasicServerRef.java:522)
at  weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:363)
at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:146)
at weblogic.rmi.internal.BasicServerRef.handleRequest(BasicServerRef.java:518)
at weblogic.rmi.internal.wls.WLSExecuteRequest.run(WLSExecuteRequest.java:118)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)

这是项目结构:

从技术上讲,从 EJB 调用/加载本机库是不可能的,还是我遗漏了什么?我确信静态块没有抛出任何异常,因为它有一个 try-catch 块,并且在 weblogic 级别没有捕获到异常。

提前致谢。

【问题讨论】:

  • 你的库在 -Djava.library.path 上吗?在您的 ejb 方法中调用 System.getProperty("java.library.path")。也许它会以某种方式被覆盖......
  • IIRC,您需要先授予DLL权限,然后JVM才能加载它。
  • @xwid 正如我所提到的,“java.libry.path”是我的 C:\Windows\System32 文件夹,我也放在了服务器库中。
  • @AlexBarker 我已授予 dll 的完全权限,但仍然是同样的错误。任何其他想法
  • DLL 和文件夹都需要权限。您显示的屏幕截图还显示了 C:\practice 中的 hellocpp.dll 文件,该文件不是 C:\Windows\System32。 java.library.path 属性告诉 Java 在哪里查找 System.load 引用的 JNI 库...这与操作系统库加载路径不同!请按照 xwid 的建议检查 java.library.path。我怀疑您忘记设置该属性或将其设置为不正确的值。

标签: java java-native-interface ejb ejb-3.0 weblogic-10.x


【解决方案1】:

我从各个方面进行了调查,牢记各个方面,最终能够解决问题。我从技术上理解可以从会话 bean(部署在 Weblogic 上)调用 JNI 库,但是从 EJB 规范的角度来看,这可能不是建议的做法,因为本机库调用使 bean 系统依赖并且它不是更便携。

由于 UnsatisfiedLinkError 具有多种性质,以下

 java.lang.UnsatisfiedLinkError: com.test.services.HelloJNICpp.sayHello()V

看起来在运行时 JNI 绑定失败了。我解决问题的方法:

检查 java.library.path -

按照朋友的建议,我写了

System.getProperty("java.library.path") 

在 bean 中并确保我的 dll 已经存在。就我而言,它是操作系统库加载路径 C:/Windows/System32(尽管我在执行 JNI 的本地实践文件夹中也有这个库)

检查文件夹权限 包含本机库的文件夹也具有读写执行权限

容器库路径

我将库放在 [WEBLOGIC_HOME]/server/lib 中,以便在运行时容器拾取。即使这样也不能解决问题

JNI 签名 我想重新审视我的 JNI 头文件和本机实现。我发现当我创建头文件(HelloJNICpp.h)时,源 JAVA 类不包含包语句。稍后我包含了 package 语句以从 Eclipse 运行它。这是一个问题是我自己的代码。所以最初是:

JNIEXPORT void JNICALL Java_HelloJNICpp_sayHello (JNIEnv *env, jobject thisObj)

使用适当的包结构创建后:

JNIEXPORT void JNICALL Java_com_test_services_HelloJNICpp_sayHello (JNIEnv *env, jobject thisObj)

这成功了!我的经验:

  1. 因此在使用 JNI 代码时必须采取额外的预防措施 具有封装结构。
  2. java.library.path 属性是 JNI 中的游戏规则改变者
  3. Eclispe java运行时和JNI运行时必须相同
  4. 对于 C++,我注意到我还必须在 java.library.path 除了我的本地库。它们用于从 MinGW 编译器套件链接。它们可以通过运行来追踪 依赖行者。

【讨论】:

    【解决方案2】:

    EJB 应该通过 JCA 资源适配器调用本机代码。

    JNI-RA-HOWTO 中有一个示例:https://wiki.ow2.org/jonas/Wiki.jsp?page=JNIRAHOWTO

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-02
      • 2019-04-09
      相关资源
      最近更新 更多