刚到新公司,就接到一个棘手的任务。想了很多办法,最后想使用Java COM桥来解决。JACOB是一个较成熟的开源项目,可以很方便的调用COM组件。搞过JNI的都知道,本地库要放到系统path中,这样,Java进程在运行中才能找到本地库并动态加载。我们可以通过环境变量System.getProperty("java.library.path")来查看当前JVM搜索本地库的路径。
这时,就会遇到一个问题,部署应用的时候要记住将本地库拷贝到环境变量path指定的路径中。一般在windows平台上直接copy到C:\WINDOWS\System32目录下了事。但要换一台机器部署怎么办?除了要把Java程序拿过去,还要记的把本地库也copy到正确的目录,真麻烦。于是想看看有什么好办法来解决这个问题。
首先,最容易想到的是,把本地库和class文件放在一起,利用Class.getResource(str)找到路径,然后加到环境java.library.path中:
代码
URL url = Foo.class.getResource("Foo.class");
String path = (new File(url.getPath())).getParent();
System.setProperty("java.library.path", path);
看上去很好,但却不能工作。查了一下ClassLoader的源代码,原来它把搜索路径定义为静态变量并只初始化一次,后面再设置java.library.path就没有用了。ClassLoader代码片断:
代码
// The paths searched for libraries
static private String usr_paths[];
static private String sys_paths[];
...
if (sys_paths == null) {
usr_paths = initializePath("java.library.path");
sys_paths = initializePath("sun.boot.library.path");
}
正在一筹莫展是,翻看JACOB的源代码,忽然有了惊喜的发现。
代码
try
{
//Finds a stream to the dll. Change path/class if necessary
InputStream inputStream = getClass().getResource("/jacob.dll").openStream();
//Change name if necessary
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();
temporaryDll.deleteOnExit();
System.load(temporaryDll.getPath());
return true;
}
catch(Throwable e)
{
e.printStackTrace();
return false;
}
高,真是好办法。和我一样,把dll放在classpath中,用Class.getResource(str).openStream()读取这个dll,然后写到temp目录中,最后用System.load(path)来动态加载。多说一句,为什么得到了jacob.dll的URL不直接去加载呢?想想看,如果把dll和class一起打成Jar包,ClassLoader还是不能加载本地库,因为System.load(path)需要的是dll的完整路径,但并不支持jar协议。还不明白,看看下面的代码:
代码
URL url = Foo.class.getResource("/java/lang/String.class");
System.out.println(url.toExternalForm());
System.out.println(url.getFile());
在我的机器上的运行结果是:
代码
jar:file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class
file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class
ClassLoader中用new File(name),当然会找不到文件。同时,看看我的第一种方法,就算能设置成功环境java.library.path,如果dll是在jar包中,还是加载不了。
到现在,你是不是觉得问题已经解决了?还没呢!jacob的很多源文件中已经写了下面的代码:
代码
static {
System.loadLibrary("jacob");
}
除非我去掉这一句重新编译jacob的源代码,否则系统还是会报错。不过,既然有了上面的想法,稍微变通一下,就可以巧妙的解决。首先找到环境java.library.path,然后把dll拷贝到其中一个路径中就行了。
代码
static {
try {
String libpath = System.getProperty("java.library.path");
if ( libpath==null || libpath.length() == 0 ) {
throw new RuntimeException("java.library.path is null");
}
String path = null;
StringTokenizer st = new StringTokenizer(libpath, System.getProperty("path.separator"));
if ( st.hasMoreElements() ) {
path = st.nextToken();
} else {
throw new RuntimeException("can not split library path:" + libpath);
}
InputStream inputStream = Foo.class.getResource("jacob.dll").openStream();
final File dllFile = new File(new File(path), "jacob.dll");
if (!dllFile.exists()) {
FileOutputStream outputStream = new FileOutputStream(dllFile);
byte[] array = new byte[8192];
for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)) {
outputStream.write(array, 0, i);
}
outputStream.close();
}
//dllFile.deleteOnExit();
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run() {
if ( dllFile.exists() ) {
boolean delete = dllFile.delete();
System.out.println("delete : " + delete);
}
}
});
} catch (Throwable e) {
throw new RuntimeException("load jacob.dll error!", e);
}
}
唯一的美中不足,在系统关闭的时候删除dll总是不能成功,试了两种办法都不行。想想也对,dll正被程序使用,当然不能删除。翻了一下API,Java好像没用提供unload本地库的功能,只好做罢。
解决了这么个小问题,罗罗嗦嗦一大篇,罪过罪过。后来这个项目又没有使用jacob,真对不起各位观众。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1772149
分享到:
相关推荐
Java通过JNI调用DLL动态库,亲测试编写
mfc42d - mfc42d.dll - DLL文件信息 DLL 文件: mfc42d 或者 mfc42d.dll DLL 名称: MFCDLL Shared Library - Debug Version 描述: mfc42d.dll是MFCDLL共享库文件,用于支持调试版本的MFC程序。 属于: MFC ...
Java通过JNI调用C++接口,Demo文档描述(里面有代码),里面有Windows下调用版和linux下调用版,经本人亲自测试,可用。
解决sqlite_jni no found in library的bug,放在tomcat的bin目录下
完整的实现java跨平台调用C程序源码,包含JAVA源码和C源码以及编译后的demo dll。将dll放到jdk bin目录下,java 项目可以直接运行。若要修改dll可以,修改C源码后重新编译生成dll。该demo处理了多线程调用c,全局...
本资源包括JAVA工程,C++工程,C#dll测试工程
利用AndroidStudio三种方式进行jni开发demo,详细介绍每种方式需要配置的文件以及注意事项
Android JNI 开发 步骤 , 绝对详细
请C++同事帮忙写个dll程序,dll去解析开发平台输出的二进制流数据,上层应用平台调用dll得到json报文,然后再去做一些业务处理。 那现在上层应用面临的问题:访问java外部功能接口实现方式(即调用dll)怎么选择及...
jni.h文件jni.h文件jni.h文件jni.h文件jni.h文件jni.h文件
说明:作为Java程序员,当与c程序员进行联合开发时会使用到JNI编程技术。此时要求Java程序员必须要会使用dll文档,以使用调试Java代码。这里介绍使用Windows OS自带的c/c++编译器(cl)怎样把.c文档编译成.exe和.dll...
JNI开发 eclipse相关开发环境的搭建
JNI开发步骤
(1)将视频特征提取算法建立相应的windows下的Dll和Linux下的So(这个两个文件大家都知是什么了吧) (2)利用jni调用这些dll。但是有一个问题,就是这里的所有库文件需要打到jar包中,因此需要将这些文件先解压到...
springboot+jna/jni调用动态so/dll库
周工总结的jni开发流程
在JAVA中如何通过JNI调用VC动态库,以及在如何在VC中调试
java通过jni调用dll动态链接库及创建dll动态链接库demo。
一个简单的android JNI 开发范例. 可以做入门学习之用
android JNI开发演示(串口收发) ndk开发,串口收发,生产so库,有代码有开发说明文档,jni开发环境配合