Jni开发时,dll文件放置的路径处理方式

刚到新公司,就接到一个棘手的任务。想了很多办法,最后想使用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中:

代码
  1. URL url = Foo.class.getResource("Foo.class");   
  2. String path = (new File(url.getPath())).getParent();   
  3. System.setProperty("java.library.path", path);  
<script type="text/javascript">render_code();</script>
看上去很好,但却不能工作。查了一下ClassLoader的源代码,原来它把搜索路径定义为静态变量并只初始化一次,后面再设置java.library.path就没有用了。ClassLoader代码片断:
代码
  1. // The paths searched for libraries   
  2. static private String usr_paths[];   
  3. static private String sys_paths[];   
  4. ...   
  5. if (sys_paths == null) {   
  6.     usr_paths = initializePath("java.library.path");   
  7.     sys_paths = initializePath("sun.boot.library.path");   
  8. }  
<script type="text/javascript">render_code();</script>
正在一筹莫展是,翻看JACOB的源代码,忽然有了惊喜的发现。
代码
  1. try  
  2. {   
  3.     //Finds a stream to the dll. Change path/class if necessary   
  4.     InputStream inputStream = getClass().getResource("/jacob.dll").openStream();   
  5.     //Change name if necessary   
  6.     File temporaryDll = File.createTempFile("jacob"".dll");   
  7.     FileOutputStream outputStream = new FileOutputStream(temporaryDll);   
  8.     byte[] array = new byte[8192];   
  9.     for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)) {   
  10.                 outputStream.write(array, 0, i);   
  11.         }   
  12.     outputStream.close();   
  13.     temporaryDll.deleteOnExit();   
  14.     System.load(temporaryDll.getPath());   
  15.     return true;   
  16. }   
  17. catch(Throwable e)   
  18. {   
  19.     e.printStackTrace();   
  20.     return false;   
  21. }  
<script type="text/javascript">render_code();</script>
高,真是好办法。和我一样,把dll放在classpath中,用Class.getResource(str).openStream()读取这个dll,然后写到temp目录中,最后用System.load(path)来动态加载。多说一句,为什么得到了jacob.dll的URL不直接去加载呢?想想看,如果把dll和class一起打成Jar包,ClassLoader还是不能加载本地库,因为System.load(path)需要的是dll的完整路径,但并不支持jar协议。还不明白,看看下面的代码:
代码
  1. URL url = Foo.class.getResource("/java/lang/String.class");   
  2. System.out.println(url.toExternalForm());   
  3. System.out.println(url.getFile());  
<script type="text/javascript">render_code();</script>
在我的机器上的运行结果是:
代码
  1. jar:file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class  
  2. file:/D:/jdk1.5.0_06/jre/lib/rt.jar!/java/lang/String.class  
<script type="text/javascript">render_code();</script>
ClassLoader中用new File(name),当然会找不到文件。同时,看看我的第一种方法,就算能设置成功环境java.library.path,如果dll是在jar包中,还是加载不了。

 

到现在,你是不是觉得问题已经解决了?还没呢!jacob的很多源文件中已经写了下面的代码:

代码
  1. static {   
  2.     System.loadLibrary("jacob");   
  3. }  
<script type="text/javascript">render_code();</script>
除非我去掉这一句重新编译jacob的源代码,否则系统还是会报错。不过,既然有了上面的想法,稍微变通一下,就可以巧妙的解决。首先找到环境java.library.path,然后把dll拷贝到其中一个路径中就行了。
代码
  1. static {   
  2.     try {   
  3.         String libpath = System.getProperty("java.library.path");   
  4.         if ( libpath==null || libpath.length() == 0 ) {   
  5.             throw new RuntimeException("java.library.path is null");   
  6.         }   
  7.                
  8.         String path = null;   
  9.         StringTokenizer st = new StringTokenizer(libpath, System.getProperty("path.separator"));   
  10.         if ( st.hasMoreElements() ) {   
  11.             path = st.nextToken();   
  12.         } else {   
  13.             throw new RuntimeException("can not split library path:" + libpath);   
  14.         }   
  15.                
  16.         InputStream inputStream = Foo.class.getResource("jacob.dll").openStream();   
  17.         final File dllFile = new File(new File(path), "jacob.dll");   
  18.         if (!dllFile.exists()) {   
  19.             FileOutputStream outputStream = new FileOutputStream(dllFile);   
  20.             byte[] array = new byte[8192];   
  21.             for (int i = inputStream.read(array); i != -1; i = inputStream.read(array)) {   
  22.                 outputStream.write(array, 0, i);   
  23.             }   
  24.             outputStream.close();   
  25.         }   
  26.         //dllFile.deleteOnExit();      
  27.         Runtime.getRuntime().addShutdownHook(new Thread(){   
  28.             public void run() {   
  29.                 if ( dllFile.exists() ) {   
  30.                     boolean delete = dllFile.delete();   
  31.                     System.out.println("delete : " + delete);   
  32.                 }   
  33.             }   
  34.         });   
  35.         } catch (Throwable e) {   
  36.             throw new RuntimeException("load jacob.dll error!", e);   
  37.     }   
  38. }  
<script type="text/javascript">render_code();</script>
唯一的美中不足,在系统关闭的时候删除dll总是不能成功,试了两种办法都不行。想想也对,dll正被程序使用,当然不能删除。翻了一下API,Java好像没用提供unload本地库的功能,只好做罢。
解决了这么个小问题,罗罗嗦嗦一大篇,罪过罪过。后来这个项目又没有使用jacob,真对不起各位观众。 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值