I'm trying to test out the facial recognition API in OpenCV. I have imported the .jar provided and it loads the DLL's correctly. The imageLibIit() function will load the DLL. I also have the provided XML files in a directory:src\main\resources\opencv
public boolean faceDetect(String inputFilename, String outputFilename){
if(!loaded){
if(!imageLibInit()){ // initializes and loads the dynamic libraries
return false;
}
}
//TODO #Austin fix image resource issues
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String xmlResource = loader.getResource("opencv/haarcascade_frontalface_alt.xml").getPath();
CascadeClassifier faceDetector = new CascadeClassifier(xmlResource);
Mat inputImage = Imgcodecs.imread(inputFilename);
ErrorUtils.println(xmlResource);
if(inputImage.empty()){
ErrorUtils.println("image is empty");
return false;
}
MatOfRect facesDetected = new MatOfRect();
faceDetector.detectMultiScale(inputImage, facesDetected);
Rect[] detections = facesDetected.toArray();
ErrorUtils.println("Faces detected in '" + inputFilename + "': " + detections.length);
for(Rect detection : detections){
Imgproc.rectangle(
inputImage,
new Point(detection.x, detection.y),
new Point(detection.x + detection.width, detection.y + detection.height),
new Scalar(0, 0, 255)
);
}
Imgcodecs.imwrite(outputFilename, inputImage);
return true;
}
I'm still getting an error:
OpenCV Error: Assertion failed (!empty()) in cv::CascadeClassifier::detectMultiScale, file C:\builds\master_PackSlaveAddon-win64-vc12-static\opencv\modules\objdetect\src\cascadedetect.cpp, line 1639
I have researched this error and each time the solution seems to be something with the resources. It's more than likely an incredibly simple issue, but I am stuck as of now.
Inspecting the OpenCV source code, we can find the following:
Internally the CascadeClassifier implementation uses class FileStorage to load the data. [1]
Class FileStorage internally uses the function fopen(...) to open the data file. [2] [3]
Since the path returned by the resouce loader ("/D:/Programming/workspaces/github/project1/HomeServer/target/classes/opencv/haarcascade_frontalface_alt.xml") is a little odd, containing the leading slash, the first suspicion leads to some issue with opening the file.
I wrote a simple little test to check this with Visual C++:
#include <cstdio>
#include <iostream>
bool try_open(char const* filename)
{
FILE* f;
f = fopen(filename, "rb");
if (f) {
fclose(f);
return true;
}
return false;
}
int main()
{
char const* path_1("/e:/foo.txt");
char const* path_2("e:/foo.txt");
std::cout << "fopen(\"" << path_1 << "\") -> " << try_open(path_1) << "\n";
std::cout << "fopen(\"" << path_2 << "\") -> " << try_open(path_2) << "\n";
return 0;
}
Output:
fopen("/e:/foo.txt") -> 0
fopen("e:/foo.txt") -> 1
Therefore the path is a culprit. According to this answer, one platform independent way is to modify the code as follows to generate a valid path:
// ...
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String xmlResource = loader.getResource("opencv/haarcascade_frontalface_alt.xml").getPath();
File file = new File(xmlResource);
xmlResource = file.getAbsolutePath());
// ...
CascadeClassifier faceDetector = new CascadeClassifier(xmlResource);
if(faceDetector.empty()){
ErrorUtils.println("cascade classifier is empty");
return false;
}
// ...
Related
Basically, I want to see if there is a first object ("sight") is visible in other images ("test") (like this), using ORB and BFMatcher in OpenCV.
ORB by itself works fine ; it detects a satisfying number of keypoints in my images.
However, when I try to use BFMatcher to compare 2 sets of descriptors, it crashes instantly without giving actual information.
Tried to print the whole keypoints and descriptors matrices (not provided here because it would be too long), and they seem to be perfectly correct ;
Loading images in Imgcodecs.IMREAD_GRAYSCALE doesn't change anything, not even ORB's results in the first place ;
Changing ORB's mask (instead of just sending a new Mat() everytime) seems to eliminate all results.
final File inputDir = new File("features/");
final File sightFile = new File(inputDir.getAbsolutePath() + "/" + "sight.jpg");
final File outputDir = new File(inputDir.getAbsolutePath() + "/" + "matching" + "/");
// In BFMatcher terms : query data
Mat sightImage = new Mat();
MatOfKeyPoint sightKeyPoints = new MatOfKeyPoint();
Mat sightDescriptors = new Mat();
// In BFMatcher terms : train data
Mat testImage = new Mat();
MatOfKeyPoint testKeyPoints = new MatOfKeyPoint();
Mat testDescriptors = new Mat();
// Loading sight image
sightImage = Imgcodecs.imread(sightFile.getAbsolutePath());
//sightImage = Imgcodecs.imread(sightFile.getAbsolutePath(), Imgcodecs.IMREAD_GRAYSCALE);
// Note : loading in greyscale (see previous line) does the exact same things
// Testing if there was any read error
if (sightImage.empty()) {
// (actually never goes here)
System.out.println("Empty image: " + sightFile.getAbsolutePath());
System.exit(0);
}
// Creating the output directory
outputDir.mkdirs();
// Init ORB detector (to get features from a single image)
ORB orb = ORB.create();
// Init BFMatcher (to compare features between 2 images)
BFMatcher bf = new BFMatcher(Core.NORM_HAMMING, true);
// Sight features (last 2 parameters are OUT parameters)
// Also, the 2nd parameter is a mask ; just throwing "new Mat()" seems to work
orb.detectAndCompute(sightImage, new Mat(), sightKeyPoints, sightDescriptors);
// SEE OUTPUT
System.out.println("sight descriptor matrix :\tsize " + sightDescriptors.size() + "x"
+ sightDescriptors.get(0, 0).length + "\tempty ? " + sightDescriptors.empty());
System.out.println("sight key points matrix :\tsize " + sightKeyPoints.size() + "x"
+ sightKeyPoints.get(0, 0).length + "\tempty ? " + sightKeyPoints.empty());
// For each file other than sightFile
for (final File fileEntry : inputDir.listFiles()) {
if (!fileEntry.isDirectory() && !fileEntry.equals(sightFile)) {
// Matches matrix
MatOfDMatch matches = new MatOfDMatch();
// Loading test image
testImage = Imgcodecs.imread(fileEntry.getAbsolutePath());
//testImage = Imgcodecs.imread(fileEntry.getAbsolutePath(), Imgcodecs.IMREAD_GRAYSCALE);
// Note : loading in greyscale (see previous line) does the exact same things
// Testing if there was any read error
if (testImage.empty()) {
// (actually never goes here)
System.out.println("Empty image: " + fileEntry.getAbsolutePath());
continue;
}
// Test image features (last 2 parameters are OUT parameters)
// Also, the 2nd parameter is a mask ; just throwing "new Mat()" seems to work
orb.detectAndCompute(testImage, new Mat(), testKeyPoints, testDescriptors);
// SEE OUTPUT
System.out.println("test image descriptor matrix :\tsize " + testDescriptors.size() + "x"
+ testDescriptors.get(0, 0).length + "\tempty ? " + testDescriptors.empty());
System.out.println("test image key points matrix :\tsize " + testKeyPoints.size() + "x"
+ testKeyPoints.get(0, 0).length + "\tempty ? " + testKeyPoints.empty());
// Match descriptors (3rd parameter is an OUT parameter)
bf.match(sightDescriptors, testDescriptors, matches);
// CRASHES HERE !!!!!
// (etc.)
}
}
Actual output :
sight descriptor matrix : size 32x500x1 empty ? false
sight key points matrix : size 1x500x7 empty ? false
test image descriptor matrix : size 32x500x1 empty ? false
test image key points matrix : size 1x500x7 empty ? false
Exception in thread "main" java.lang.Exception: unknown exception
at org.opencv.features2d.DescriptorMatcher.match_1(Native Method)
at org.opencv.features2d.DescriptorMatcher.match(DescriptorMatcher.java:245)
at testopencv.DetectionTest.FeatureMatchingTest(DetectionTest.java:235)
at testopencv.DetectionTest.main(DetectionTest.java:29)
Well, the sysout calls seem to be OK, the exception is obviously not...
The picture linked below shows the specific exception I'm getting. I'm not quite sure why I'm having this particular issue as I've built everything in the same directory, so the library file is there. From what I understand this has something to do with what I'm returning to my main method from my c++ function.
What I'm essentially trying to do is pass the name (printId) of the recognized person, as a string, from my c++ function to java.
Picture of command line:
Here's my C++ code:
#include <jni.h>
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/face.hpp"
#include "opencv2/face/facerec.hpp"
#include <vector>
#include <string>
#include "recognitionJNI.h"
#include <fstream>
#include <sstream>
using namespace cv;
using namespace std;
String face_cascade_name = "/Users/greg/Downloads/opencv-3.4.2/data/haarcascades/haarcascade_frontalface_alt.xml";
CascadeClassifier face_cascade;
String fn_csv = "/Users/greg/Desktop/faceBuild/faceRecognition/faceRecognition/csv.txt";
//User Defined Function for reading csv
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
ifstream file(filename.c_str(), ifstream::in); //opens file for reading
if(!file) {
cout << "ERROR: There was a problem loading the csv file" << endl;
}
string line, path, classlabel;
while(getline(file,line)) {
stringstream liness(line);
getline(liness, path, separator); //read stream object up to the semicolon
getline(liness, classlabel); //read the rest of stream object up to null terminated character
//make sure that the filepath and userID are not empty
if(!path.empty() && !classlabel.empty()) {
images.push_back(imread(path,0)); //appends grayscale image to images vector
labels.push_back(atoi(classlabel.c_str())); //appends userID to labels vector
}
}
}
JNIEXPORT jstring JNICALL Java_testJNIString_userName(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
const char *inCStr = env->GetStringUTFChars(inJNIStr, NULL);
if (NULL == inCStr) return NULL;
string outCppStr;
cout << "In C++, the received string is: " << inCStr << endl;
env->ReleaseStringUTFChars(inJNIStr, inCStr);
string printId;
vector<Mat> images; //This vector will hold the images
vector<int> labels; //This vector will hold the userID
//read the csv file contain image paths and userID's
try {
read_csv(fn_csv, images, labels);
} catch (Exception& e) {
cerr << "Error opening file\"" << fn_csv << "\". Reason: " << e.msg << endl;
exit(1);
}
//we'll need to resize the images to their origal size
//These two lines capture the length and width of the mat object
int im_width = images[0].cols;
int im_height = images[0].rows;
for(int j=0; j < images.size(); j++) {
resize(images[j],images[j],Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
}
//int numComponents = 2;
//double threshold = 10.0;
//creats a faceRecognizer to train with given images
Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
model->train(images, labels);
string camera_msg = "No camera found";
Mat webcam; // creates Mat object for to store frames
VideoCapture cap(0); // opens default webcam
if(!cap.isOpened()) {
return env->NewStringUTF(camera_msg.c_str());
}
face_cascade.load(face_cascade_name); //loads xml file into classifier
//load capture device into Mat object
while (cap.read(webcam)) {
vector<Rect> faces;
Mat frame_gray; //will be used to store grayscale copy of webcam
cvtColor(webcam, frame_gray, COLOR_BGR2GRAY); //coverts Mat object frames into grayscale
equalizeHist(frame_gray, frame_gray); //maps input distrubution to more uniform distribution
//locate the faces in the frame
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 5, 0|CASCADE_SCALE_IMAGE,Size(30,30));
for(size_t i=0; i < faces.size(); i++) {
Rect face_i = faces[i]; //process faces by frame
Mat face = frame_gray(face_i); //takes the face from the live images
//resize faces for prediction
Mat face_resized;
resize(face,face_resized,Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
int prediction = model->predict(face_resized); //predict based on resize faces
if(prediction == 1 ) {
printId = "Matthew";
}
else if (prediction == 2) {
printId = "Greg";
return env->NewStringUTF(printId.c_str());
}
else if(prediction != 1 || 2 ){
printId = "Unknown";
}
rectangle(webcam, face_i, CV_RGB(0,255,0), 1); //draws a rectangle around the face
string box_text = "Prediction = " + printId;
int pos_x = std::max(face_i.tl().x - 10, 0);
int pos_y = std::max(face_i.tl().y - 10, 0);
putText(webcam, box_text, Point(pos_x,pos_y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(0,255,0), 1);
}
imshow("Webcam", webcam);
waitKey(1);
destroyAllWindows();
}
return env->NewStringUTF(printId.c_str());
}
Here's my Java code:
public class recognitionJNI{
static {
System.loadLibrary("recogjni");
}
private native String userName(String msg);
public static void main(String args[]) {
String result = new recognitionJNI().userName("Pass arg from c++ function");
System.out.println(result);
}
}
Try regenerating the header file, it looks like you changed your class name in the meantime and the name is no longer up to date. The name I get from that java class is:
Java_recognitionJNI_userName
But you have
Java_testJNIString_userName
When creating an ActiveXComponent using JACOB I get the following error.
com.jacob.com.ComFailException: Can't co-create object
at com.jacob.com.Dispatch.createInstanceNative(Native Method)
at com.jacob.com.Dispatch.<init>(Dispatch.java:99)
at com.jacob.activeX.ActiveXComponent.<init>(ActiveXComponent.java:58)
at com.paston.jacobtest.RidderIQ.main(RidderIQ.java:30)
The COM object which I need to use from a program which doesn't register its DLLs by itself during installation.
To register the DLL I used the 64bit version of RegAsm according to this article that could help. Also, I tried to load every DLL in of the external program because I suspected that there could be "something" wrong with loading the dependencies.
Here is my current code:
public static void main(String[] args) {
String dllDir = "C:\\Program Files (x86)\\Ridder iQ Client\\Bin\\";
File folder = new File( dllDir );
for (final File fileEntry : folder.listFiles()) {
String str = fileEntry.getName();
if (str.substring(str.lastIndexOf('.') + 1).equals("dll")) {
System.out.println(fileEntry.getName());
System.load(dllDir + str);
}
}
try {
ActiveXComponent example = new ActiveXComponent("RidderIQSDK");
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
When changing the name to the clsid I get a different exception.
com.jacob.com.ComFailException: Can't find moniker
at com.jacob.com.Dispatch.createInstanceNative(Native Method)
at com.jacob.com.Dispatch.<init>(Dispatch.java:99)
at com.jacob.activeX.ActiveXComponent.<init>(ActiveXComponent.java:58)
at com.paston.jacobtest.RidderIQ.main(RidderIQ.java:28)
I got JACOB to work with my code in another test using the system's Random object.
ActiveXComponent random = new ActiveXComponent("clsid:4E77EC8F-51D8-386C-85FE-7DC931B7A8E7");
Object obj = random.getObject();
Object result = Dispatch.call((Dispatch) obj, "Next");
System.out.println("Result: "+result);
I tried all solution and finally succeeded to crack the code related to JACOB. Create your code as per below sample code.
public static void main(String[] args) {
String libFile = System.getProperty("os.arch").equals("amd64") ? "jacob-1.17-x64.dll" :"jacob-1.17-x86.dll";
try{
/**
* Reading jacob.dll file
*/
InputStream inputStream = certificatemain.class.getResourceAsStream(libFile);
/**
* Step 1: Create temporary file under <%user.home%>\AppData\Local\Temp\jacob.dll
* Step 2: Write contents of `inputStream` to that temporary file.
*/
File temporaryDll = File.createTempFile("jacob", ".dll");
FileOutputStream outputStream = new FileOutputStream(temporaryDll);
byte[] array = new byte[8192];
for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)){
outputStream.write(array, 0, i);
}
outputStream.close();
/* Temporary file will be removed after terminating-closing-ending the application-program */
System.setProperty(LibraryLoader.JACOB_DLL_PATH, temporaryDll.getAbsolutePath());
LibraryLoader.loadJacobLibrary();
ActiveXComponent comp=new ActiveXComponent("Com.Calculation");
System.out.println("The Library been loaded, and an activeX component been created");
int arg1=100;
int arg2=50;
//using the functions from the library:
int summation=Dispatch.call(comp, "sum",arg1,arg2).toInt();
System.out.println("Summation= "+ summation);
}catch(Exception e){
e.printStackTrace();
}
}
Now let me tell you how to register your DLL. I also followed same article you mentioned but not working when you are dealing with applet.
Go to x86 framework using command line.
C:\Windows\Microsoft.NET\Framework\v2.0.50727
to register do same as
regasm.exe path_to_your_dll.dll /codebase
Don't pass any other flag except /codebase. You are done with it... Still you find any problem let me know...
I'm creating a program in Java that will interface with a C library to take images from hardware and display with OpenGL (using JOGL). So the workflow is this:
Hardware -> C -> disk image file -> Java -> JOGL
I have the Java -> JOGL part working fine. The images are displayed fully and I can load multiple images at a time. I also have the Hardware -> C working as well, and a temporary viewer in C is showing that the images are being created just fine.
The crux of the problem is this: I want to be able to launch the main() method of the Java program in C and then display the image using only JNI code in C (using static methods I've created). However, when I do this, the image is truncated, where I only get the top 20 or so rows. I do know that I'm loading the entire image because I can check the pixel values for every pixel in the image. Only the display is truncated. The same number of pixels are shown for every image I load.
This is the C code in a nutshell:
int main () { ...
HMODULE * jvm_dll;
//HWND hWnd = GetConsoleWindow();
//ShowWindow( hWnd, SW_HIDE );
env = create_vm(&jvm, jvm_dll);
if (env == NULL) { return 1; }
//Create a new String array with one blank String
cls = (*env)->FindClass(env,"java/lang/String");
arg = (*env)->NewStringUTF(env,"");
args = (jobjectArray)(*env)->NewObjectArray(env, 1, cls, arg);
//Call the main method with the String array argument
cls = (*env)->FindClass(env, "path/to/package/program");
mID = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
(*env)->CallStaticVoidMethod(env, cls, mID, args);
PrintStackTrace(env);
blockAndClose(jvm, env, jvm_dll);
return ret;
}
int blockAndClose() {...
int ret = 0;
if (jvm == 0 || env == 0) {
FreeLibrary(*jvm_dll);
return 1;
}
ret = (*jvm)->DestroyJavaVM(jvm);
if(jvm_dll) {
FreeLibrary(*jvm_dll);
jvm_dll = 0;
}
env = 0;
jvm = 0;
return ret;
}
I know that I've only posted the C portion, but the Java portion is working when I run it purely in Java. At this point, the C portion is simply a "launcher" of sorts, so I'm wondering why it affects the running of the code. Any suggestions?
EDIT:
Here's the code for loading images. I use JAI to load in the image as a PlanarImage type (TiledImage class is a subclass).
tempI = JAI.create("fileload", path);
DataBufferUShort dbOut = null;
ColorModel CM = PlanarImage.getDefaultColorModel(DataBuffer.TYPE_USHORT, tempI.getNumBands());
SampleModel SM = CM.createCompatibleSampleModel(tempI.getWidth(), tempI.getHeight());
//Ensure that the buffer is in the internal format (USHORT)
if (tempI.getData().getDataBuffer().getDataType() != DataBuffer.TYPE_USHORT) {
DataBuffer dBIn = tempI.getData().getDataBuffer();
short [] dBPixels = new short[dBIn.getSize()];
switch(dBIn.getDataType()) {
case DataBuffer.TYPE_BYTE:
DataBufferByte dBByte = (DataBufferByte)dBIn;
byte [] pByte = dBByte.getData();
for (int b = 0; b < pByte.length; b++)
{ dBPixels[b] = (short)((double)pByte[b] / 0xFF * 0xFFFF); }
dbOut = new DataBufferUShort(dBPixels, dBPixels.length);
break;
case DataBuffer.TYPE_SHORT:
DataBufferShort dBShort = (DataBufferShort)dBIn;
dBPixels = dBShort.getData();
dbOut = new DataBufferUShort(dBPixels, dBPixels.length);
break;
} //SWITCH DATA TYPE --END
WritableRaster rs = Raster.createWritableRaster(SM, dbOut, new Point(0,0));
tempI = new TiledImage(0,0,tempI.getWidth(),tempI.getHeight(),0,0,SM,CM);
((TiledImage)tempI).setData(rs);
}
I want to load my own native libraries in my java application. Those native libraries depend upon third-party libraries (which may or may not be present when my application is installed on the client computer).
Inside my java application, I ask the user to specify the location of dependent libs. Once I have this information, I am using it to update the "LD_LIBRARY_PATH" environment variable using JNI code. The following is the code snippet that I am using to change the "LD_LIBRARY_PATH" environment variable.
Java code
public static final int setEnv(String key, String value) {
if (key == null) {
throw new NullPointerException("key cannot be null");
}
if (value == null) {
throw new NullPointerException("value cannot be null");
}
return nativeSetEnv(key, value);
}
public static final native int nativeSetEnv(String key, String value);
Jni code (C)
JNIEXPORT jint JNICALL Java_Test_nativeSetEnv(JNIEnv *env, jclass cls, jstring key, jstring value) {
const char *nativeKey = NULL;
const char *nativeValue = NULL;
nativeKey = (*env)->GetStringUTFChars(env, key, NULL);
nativeValue = (*env)->GetStringUTFChars(env, value, NULL);
int result = setenv(nativeKey, nativeValue, 1);
return (jint) result;
}
I also have corresponding native methods to fetch the environment variable.
I can successfully update the LD_LIBRARY_PATH (this assertion is based on the output of C routine getenv().
I am still not able to load my native library. The dependent third-party libraries are still not detected.
Any help/pointers are appreciated. I am using Linux 64 bit.
Edit:
I wrote a SSCE (in C) to test if dynamic loader is working. Here is the SSCE
#include
#include
#include
#include
int main(int argc, const char* const argv[]) {
const char* const dependentLibPath = "...:";
const char* const sharedLibrary = "...";
char *newLibPath = NULL;
char *originalLibPath = NULL;
int l1, l2, result;
void* handle = NULL;
originalLibPath = getenv("LD_LIBRARY_PATH");
fprintf(stdout,"\nOriginal library path =%s\n",originalLibPath);
l1 = strlen(originalLibPath);
l2 = strlen(dependentLibPath);
newLibPath = (char *)malloc((l1+l2)*sizeof(char));
strcpy(newLibPath,dependentLibPath);
strcat(newLibPath,originalLibPath);
fprintf(stdout,"\nNew library path =%s\n",newLibPath);
result = setenv("LD_LIBRARY_PATH", newLibPath, 1);
if(result!=0) {
fprintf(stderr,"\nEnvironment could not be updated\n");
exit(1);
}
newLibPath = getenv("LD_LIBRARY_PATH");
fprintf(stdout,"\nNew library path from the env =%s\n",newLibPath);
handle = dlopen(sharedLibrary, RTLD_NOW);
if(handle==NULL) {
fprintf(stderr,"\nCould not load the shared library: %s\n",dlerror());
exit(1);
}
fprintf(stdout,"\n The shared library was successfully loaded.\n");
result = dlclose(handle);
if(result!=0) {
fprintf(stderr,"\nCould not unload the shared library: %s\n",dlerror());
exit(1);
}
return 0;
}
The C code also does not work. Apparently, the dynamic loader is not rereading the LD_LIBRARY_PATH environment variable. I need to figure out how to force the dynamic loader to re-read the LD_LIBRARY_PATH environment variable.
See the accepted answer here:
Changing LD_LIBRARY_PATH at runtime for ctypes
In other words, what you're trying to do isn't possible. You'll need to launch a new process with an updated LD_LIBRARY_PATH (e.g., use ProcessBuilder and update environment() to concatenate the necessary directory)
This is a hack used to manipulate JVM's library path programmatically. NOTE: it relies on internals of ClassLoader implementation so it might not work on all JVMs/versions.
String currentPath = System.getProperty("java.library.path");
System.setProperty( "java.library.path", currentPath + ":/path/to/my/libs" );
// this forces JVM to reload "java.library.path" property
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
This code uses UNIX-style file path separators ('/') and library path separator (':'). For cross-platform way of doing this use System Properties to get system-specific separators: http://download.oracle.com/javase/tutorial/essential/environment/sysprop.html
I have successfully implemented something similar for CollabNet Subversion Edge, which depends on the SIGAR libraries across ALL Operating Systems (we support Windows/Linux/Sparc both 32 bits and 64 bits)...
Subversion Edge is a web application that helps one managing Subversion repositories through a web console and uses SIGAR to the SIGAR libraries helps us provide users data values directly from the OS... You need to update the value of the property "java.library.path" at runtime. (https://ctf.open.collab.net/integration/viewvc/viewvc.cgi/trunk/console/grails-app/services/com/collabnet/svnedge/console/OperatingSystemService.groovy?revision=1890&root=svnedge&system=exsy1005&view=markup Note that the URL is a Groovy code, but I have modified it to a Java here)...
The following example is the implementation in URL above... (On Windows, your user will be required to restart the machine if he/she has downloaded the libraries after or downloaded them using your application)... The "java.library.path" will update the user's path "usr_paths" instead of System path "sys_paths" (permissions exception might be raised depending on the OS when using the latter).
133/**
134 * Updates the java.library.path at run-time.
135 * #param libraryDirPath
136 */
137 public void addDirToJavaLibraryPathAtRuntime(String libraryDirPath)
138 throws Exception {
139 try {
140 Field field = ClassLoader.class.getDeclaredField("usr_paths");
141 field.setAccessible(true);
142 String[] paths = (String[])field.get(null);
143 for (int i = 0; i < paths.length; i++) {
144 if (libraryDirPath.equals(paths[i])) {
145 return;
146 }
147 }
148 String[] tmp = new String[paths.length+1];
149 System.arraycopy(paths,0,tmp,0,paths.length);
150 tmp[paths.length] = libraryDirPath;
151 field.set(null,tmp);
152 String javaLib = "java.library.path";
153 System.setProperty(javaLib, System.getProperty(javaLib) +
154 File.pathSeparator + libraryDirPath);
155
156 } catch (IllegalAccessException e) {
157 throw new IOException("Failed to get permissions to set " +
158 "library path to " + libraryDirPath);
159 } catch (NoSuchFieldException e) {
160 throw new IOException("Failed to get field handle to set " +
161 "library path to " + libraryDirPath);
162 }
163 }
The Bootstrap services (Groovy on Grails application) class of the console runs a service and executes it with the full path to the library directory... UNiX-based servers do not need to restart the server to get the libraries, but Windows boxes do need a server restart after the installation. In your case, you would be calling this as follows:
String appHomePath = "/YOUR/PATH/HERE/TO/YOUR/LIBRARY/DIRECTORY";
String yourLib = new File(appHomePath, "SUBDIRECTORY/").getCanonicalPath();
124 try {
125 addDirToJavaLibraryPathAtRuntime(yourLib);
126 } catch (Exception e) {
127 log.error("Error adding the MY Libraries at " + yourLib + " " +
128 "java.library.path: " + e.message);
129 }
For each OS you ship your application, just make sure to provide a matching version of the libraries for the specific platform (32bit-Linux, 64bit-Windows, etc...).