Reputation: 57
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.
Upvotes: 0
Views: 1201
Reputation: 1985
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
Upvotes: 1
Reputation: 57
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:
Upvotes: 1