简体   繁体   中英

EJB to JNI (C++) call

I am trying to call a native library on windows (dll) from EJB using JNI. I have read lots of blogs and website suggests that making JNI calls from EJB is not allowed in EJB spec. There are vendor specific exceptions though. I am using weblogic as EJB container. If you can guide/suggest me on this area. I know resource adapter is an option but it is an herculean task to implement for simple requirements. Here is my approach.

I developed a simple JNI which calls a C++ native library (also build by me) and prints the output:

在此处输入图片说明

So JNI class HelloJNICpp calls the dll and invokes the a native method sayHello() , implemented in C++ , which gives the output

Now, I implemented a session bean ( TestEJBBean.java ), a wrapper class HelloJNICpp.java (like JNI) which is called by the bean. Finally a stand alone java client TestEJBClient.java to test the bean, whether it can be invoked and native could be called.

Here is the code details:

Remote Interface

package com.test.services;

import javax.ejb.Remote;

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

Session 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
        }
}

Wrapper class serves as 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 test client

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;
    }
}

I placed the dll library in all possible locations like java library path can include like C:\\Windows\\System32 , weblogic domain lib, even I bundled that with EJB application and deployed it. When I execute the client I get UnsatisfiedLink Error.

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)

Here is the project structure:

在此处输入图片说明

Is it technically not possible to call/load a native library from EJB or am I missing something? I am sure thar static block is not throwing any exception because, it has a try-catch block and no exception is caught in weblogic level.

Thanks in advance.

I investigated from all corners, keeping all the aspects in mind and finally able to resolve the issue. I understood technically it is possible to call a JNI library from a session bean (deployed on Weblogic), however it may not be a suggested practice from a EJB spec point of view because the native library call makes the bean system dependent and it is no more portable.

As UnsatisfiedLinkError has various natures, the following

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

looks like at runtime JNI binding is failing. Approaches I took to resolve the issue:

Checking java.library.path -

As friends suggested, I wrote

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

inside the bean and make sure my dll was already there. For my case it was OS library load path C:/Windows/System32 (eventhough I had this library also in my local practice folder where from I was executing the JNI)

Checking the folder permission The folder containing the native library was also there with read-write-execute permission

Container library path

I placed the library in [WEBLOGIC_HOME]/server/lib so that at runtime container picks up. Even this was not resolving the issue

JNI signature I thought of revisiting my JNI header files and native implementations. I discovered the when I created the header file (HelloJNICpp.h) the source JAVA class did not contain the package statement. I included the package statement later to run it from Eclipse. This was a problem is my code it self. So initially it was:

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

later after creating with proper package structure:

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

and this worked! My learnings:

  1. So extra precaution has to be exercised when working with JNI code with packaged structure.
  2. java.library.path property is game changer in JNI
  3. Eclispe java runtime and runtime of JNI has to be same
  4. For C++, I noticed I had to place some additional system dependent libraries also in java.library.path apart from my native library. They were used to link from MinGW compiler suite. They can be traced by running dependencyWalker.

An EJB should call native code via a JCA Resource Adapter.

A sample is in JNI-RA-HOWTO : https://wiki.ow2.org/jonas/Wiki.jsp?page=JNIRAHOWTO

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM