5
votes

Problem presentation

I try to install OpenCV as global module of a JBoss-as instance. The versions are:

  • JBoss-as: 7.1.1 Final
  • OpenCV: 2.4.5 (compiled with Java support)

I started from a fresh installation of Ubuntu Server 12.04 64 bits with only JBoss-as and OpenCV installed.

Preamble

The OpenCV java wrapper use JNI calls. Thus two things are required:

  • opencv-245.jar
  • libopencv_java245.so

And available in /usr/share/OpenCV/java/ (regarding to installation)

I also point some observations:

  1. The installation of JBoss is correct (applications can be deployed and are working)
  2. The compilation and installation of OpenCV with java support is correct (Java class using OpenCV are working)
  3. A basic web application using OpenCV and deployed, with maven, on JBoss-as works (the opencv-245.jar is listed as dependency in pom.xml, and thus packaged into the war)

Problem description

As soon as I define OpenCV as JBoss global module (setting <scope>provided</scope> in pom.xml) this exception is raised:

java.lang.UnsatisfiedLinkError: org.opencv.core.Mat.n_Mat()J
    org.opencv.core.Mat.n_Mat(Native Method)
    org.opencv.core.Mat.<init>(Mat.java:441)
    WS.printMessage(WS.java:15)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:616)
    org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:155)
    org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:257)
    org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:222)
    org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:211)
    org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:525)
    org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:502)
    org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:119)
    org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:208)
    org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:55)
    org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:50)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:847)

It appears that the OpenCV jar library is found because the exception is raised from it. Also it does not complains about some library not found like (taken from first link at the end):

java.lang.UnsatisfiedLinkError: no xxxx in java.library.path

thus I guess the libopencv_java245.so is not the problem. The precise configuration is described below.

Configurations

I defined the org.opencv module in modules/org/opencv/main/module.xml:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="org.opencv">
    <resources>
        <resource-root path="opencv-245.jar"/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
    </dependencies>
</module>

Then I put in the same folder opencv-245.jar and also libopencv_java245.so in lib/linux-x86_64/ subfolder (as explained in Native Library)

To define this module as global I modified in standalone/configuration/standalone.xml:

<subsystem xmlns="urn:jboss:domain:ee:1.0">
    <global-modules>
        <module name="org.opencv" slot="main"/>
    </global-modules>
</subsystem>

Finally to use the global module I set in src/main/webapp/WEB-INF/jboss-deployment-structure.xml:

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.opencv" />
        </dependencies>
    </deployment>
</jboss-deployment-structure>

I also reminds that I have put <scope>provided</scope> in my maven pom.xml.

Also, putting:

System.loadLibrary("opencv_java245");

or not in the code doesn't change anything.

Extra observations

I also noticed that with an ear application, composed of one war and one jar, even the procedure described in point 3 in "Preamble" does not work and give the same exception as above. Maybe this information can help.

Someone have some pointers or solutions?

Related questions

3

3 Answers

6
votes

I finally solve the problem and write here the answer to help other people.

The problem wasn't in the path of the libopencv_java245.so, but in the JBoss Classloaders.

For the case decribed in Preamble point 3 (which worked), the ClassLoader that load the war file is the same that load opencv-245.jar (which is embedded into the war), and the call to System.loadLibrary("opencv_java245") in my code has effect on this same ClassLoader, because it has loaded the Class containing this method call. All has effect on the same ClassLoader and everything work.

Now the reason why it is not working with an ear follow up from the fact that an ear has its own ClassLoader, and then each subdeployment as another one. The first ClassLoader deploy the ear, which contains the opencv-245.jar dependecy, then another ClassLoader deploy the war contained into the ear. Because the call System.loadLibrary("opencv_java245") is in the war, the effect of this command has effect on the ClassLoader of the war, but the opencv-245.jar is loaded into the ear ClassLoader. Thus when trying to call native library, java can't find the link because they are on different ClassLoader.

Finally the point of interest here is for a JBoss module. When describing a module as configured in my initial question, this is a high level JBoss ClassLoader which load the opencv-245.jar. The ClassLoader will also automatically know where to search for native libraries: in $MODULE_PATH/lib/linux-x86_64/. But the problem is to load the library. The call to System.loadLibrary("opencv_java245") must be done in the same ClassLoader that have charged opencv-245.jar. Thus it is not possible to load library like this in your code:

static {
    System.loadLibrary("opencv_java245");
}

because it will has effect on the ClassLoader that have load your Class, not on the JBoss one. The solution is to modify the opencv-245.jar and add into it a org.opencv.core.Loader class for instance, which have only one method:

package org.opencv.core

class Loader
{
    public static void loadLibrary(String name)
    {
        System.loadLibrary(name);
    }
}

Then in your class you can put:

static {
    Loader.loadLibrary("opencv_java245");
}

And the System.loadLibrary call being placed in opencv-245.jar will have effect one the same ClassLoader that load the opencv-245.jar. Then native call are correctly linked because both library, the jar and the so, were loaded in the same ClassLoader.

1
votes

If you are using OpenCV (org.openpnp package) 3.4.2+ instead of:

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

you can use:

OpenCV.loadLocally();

I'm using jboss wildfly, and had problem when doing more than one deploy. Native classes were already loaded, but this fixed the problem.

0
votes

Add the libopencv_java245.so file to the same directory you created for your OpenCV module. then add it as a resource so it will be added to the classpath and be available at runtime.

So your module.xml will look like this now:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="org.opencv">
    <resources>
        <resource-root path="opencv-245.jar"/>
        <resource-root path="."/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
    </dependencies>
</module>