Use OpenCV Classes in Spring Bean XML Configuration
Today I ran into an infuriating issue that lasted for several hours. Here's the
stack trace I was given when trying to run mvn tomcat:run
:
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.opencv.ml.CvSVM]: Constructor threw exception; nested exception is java.lang.UnsatisfiedLinkError: org.opencv.ml.CvSVM.CvSVM_0()J at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:162) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:76) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:990) ... 56 more Caused by: java.lang.UnsatisfiedLinkError: org.opencv.ml.CvSVM.CvSVM_0()J at org.opencv.ml.CvSVM.CvSVM_0(Native Method) at org.opencv.ml.CvSVM.<init>(CvSVM.java:63) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:534) at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147) ... 58 more
Linker errors are normally because you failed to call System.loadLibrary
and
then your JNI fails since it knows about your classes via the Jar wrappers around
the native code (and will compile) but at runtime you're out of luck. My problem
was doubly more confusing because at runtime my OpenCV code worked fine, but
stopped when I tried to put one of the classes into an XML bean. Then the error
above happened.
So how do you fix it? Simple, you need to call System.loadLibrary
... from
the XML. But how to do this obvious thing? For classes that depend on native
libraries the general pattern you do is the following:
import some.native.library.*; public class SomethingThatNeedsNativeSupport { static { System.loadLibrary(NATIVE_LIBRARY_NAME); } }
But for XML configuration? It's surprisingly difficult to google for, and it wasn't until I talked to one of my coworkers about the issue that he told me the obvious answer: The depends-on property pointed at a loader class.
I had already thought to box the classes that were failing as beans in my own wrapper implementations since I could run the classes fine in the actual Java code (luckily I didn't have this guys issue), but a loader class was way easier, and now I can use XML to configure the OpenCV classes. Here's the code:
//OpenCVLoader.java package info.ethanjoachimeldridge.cv; import org.opencv.core.*; public class OpenCVLoader { static { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); } }
Throw that into your package and then do something like the following in your XML:
<bean id="cvLibLoader" class="info.ethanjoachimeldridge.cv.OpenCVLoader" /> <bean id="svm" class="org.opencv.ml.CvSVM" depends-on="cvLibLoader"/>
The depends-on
property of the bean will force that bean to be initialized before
the other, and therefore the library will be loaded when Spring gets around to
loading the class.
Hope this helps anyone else out there who's using Spring Batch and OpenCV together.