gr8vyguy@Blogjava

分析SWT3.3加载原生库的过程


SWT3.3 M4(2006年12月15日)新增加的功能之一是自动加载原生库,特别是从SWT的Jar文件中加载原生库的功能,大大方便了我们发布基于SWT的Java程序。SWT是怎么实现这个功能的呢?  理解其中的原理后,您也可以在您自己的程序中实现类似的功能。
SWT负责加载原生库的方法是Library类的loadLibrary (String name)
static  {
    Library.loadLibrary( " swt " );
}

Library.loadLibrary()做的第一件事在库名上加上版本号,比如swt-win32-3325。
如果您的原生库文件名不包含版本号,您可以省略这一步。

String version = System.getProperty ("swt.version");
if (version == null) {
version = "" + MAJOR_VERSION;
/* Force 3 digits in minor version number */
if (MINOR_VERSION < 10) {
    version += "00"; //$NON-NLS-1$
    } else {
    if (MINOR_VERSION < 100) version += "0"; 
    }
    version += MINOR_VERSION;        
    /* No "r" until first revision */
if (REVISION > 0) version += "r" + REVISION;
}
libName1 = name + "-" + Platform.PLATFORM + "-" + version;  
libName2 = name + "-" + Platform.PLATFORM

第二件事是加上操作系统相关的库扩展名,比如Windows的.dll, Unix/Linux的.so.
调用System.mapLibraryName (libName1).
mappedName1  =  System.mapLibraryName (libName1);
mappedName2  =  System.mapLibraryName (libName2);

然后按顺序从swt.library.path和java.library.path指定的目录下寻找。如若没有成功,
并且没有设置swt.library.path,那么再找一下java.io.tmpdir指定的目录。要是还没有成功,Library将尝试
将SWT的Jar文件中的原生库解压到swt.library.path目录下或者java.io.tmpdir目录下。
/*  Try loading library from java library path  */
f  (load (libName1))  return ;
if  (mapName  &&  load (libName2))  return ;
    
/*  Try loading library from the tmp directory  if swt library path is not specified  */
if  (path  ==   null ) {
        path  =  System.getProperty ( " java.io.tmpdir " ); 
        path  =   new  File (path).getAbsolutePath ();
         if  (load (path  +  SEPARATOR  +  mappedName1))  return ;
         if  (mapName  &&  load (path  +  SEPARATOR  +  mappedName2))  return ;
}
        
/*  Try extracting and loading library from jar  */
if  (path  !=   null ) {
         if  (extract (path  +  SEPARATOR  +  mappedName1, mappedName1))  return ;
         if  (mapName  &&  extract (path  +  SEPARATOR  +  mappedName2, mappedName2))  return ;
}
    
/*  Failed to find the library  */
throw new UnsatisfiedLinkError ( " no  "   +  libName1  +   "  or  "   +  libName2 +   "  in swt.library.path, java.libary.path or the jar file " );


extract (path+SEPARATOR+mappedName1, mappedName1)将mappedName1从Jar中解压到
path+SEPARATOR+mappedName1。最后如果还是没有成功,甩出一个UnsatisfiedLinkError。


整个过程有意思的只是extract()方法,下面来看看extract
static   boolean  extract (String fileName, String mappedName)  {
    FileOutputStream os  =   null ;
    InputStream is  =   null ;
    File file  =   new  File(fileName);
     try   {
         if  ( ! file.exists ())  {
            is  =  Library. class .getResourceAsStream ( " / "   +  mappedName);
             if  (is  !=   null )  {
                 int  read;
                 byte  [] buffer  =   new   byte  [ 4096 ];
                os  =   new  FileOutputStream (fileName);
                 while  ((read  =  is.read (buffer))  !=   - 1 )  {
                    os.write(buffer,  0 , read);
                }
                os.close ();
                is.close ();
                 if  ( ! Platform.PLATFORM.equals ( " win32 " ))  {
                     try   {
                        Runtime.getRuntime ().exec (
                new  String [] { " chmod " ,  " 755 " , fileName} ).waitFor();
                    }   catch  (Throwable e)  {}
                }
                 if  (load (fileName))  return   true ;
            }
        }
    }   catch  (Throwable e)  {
         try   {
             if  (os  !=   null ) os.close ();
        }   catch  (IOException e1)  {}
         try   {
             if  (is  !=   null ) is.close ();
        }   catch  (IOException e1)  {}
    }
     if  (file.exists ()) file.delete ();
     return   false ;
}

Library.class.getResourceAsStream ("/" + mappedName)返回指向Jar根目录下mappedName文件的
InputStream.个人感觉,extract写得有点乱,if套着if,if套着try,当然并不是很糟糕,本身这是一个比较简单的函数。
在我自己的程序中,重写了extract(改名为extractAndLoad),相对来说要清爽一些,请大家比较。 
通过上面的分析,我们可以很简单实现我们自己的loadLibrary
import  org.eclipse.swt.SWT;

import  java.io.File;
import  java.io.FileOutputStream;
import  java.io.InputStream;

/**
 * The method loadLibrary load a native library in the order 
 * from java.library.path, os.library.path, tmpdir and from jar.
 *  
 *  @author  pan
  */
public   class  Library {

     static   final  String SEPARATOR  =  System.getProperty( " file.separator " );

     public   static   void  loadLibrary(String name) {
         //  Try loading library from os library path
        String path  =  System.getProperty( " os.library.path " );
         if  (path  !=   null ) {
            path  =   new  File(path).getAbsolutePath();
             if  (_load(System.mapLibraryName(path  +  SEPARATOR  +  name)))
                 return ;
        }

         //  Try loading library from java library path
         if  (_load(name))
             return ;

         //  Try loading library from the tmp directory if os library path is not specified
         if  (path  ==   null ) {
            path  =  System.getProperty( " java.io.tmpdir " );
            path  =   new  File(path).getAbsolutePath();
             if  (_load(System.mapLibraryName(path  +  SEPARATOR  +  name)))
                 return ;
        }

         //  Try extracting and loading library from jar
         if  (path  !=   null
                 &&  loadFromJar(System.mapLibraryName(path  +  SEPARATOR  +  name),
                        System.mapLibraryName(name)))
             return ;

         //  Failed to find the library
         throw   new  UnsatisfiedLinkError( " no  "   +  name
                 +   "  in java.libary.path, os.library.path or jar file " );
    }

     private   static   boolean  _load(String libName) {
         try  {
             if  (libName.indexOf(SEPARATOR)  !=   - 1 ) {
                System.load(libName);
            }  else  {
                System.loadLibrary(libName);
            }
             return   true ;
        }  catch  (UnsatisfiedLinkError e) {
        }
         return   false ;
    }

     private   static   boolean  loadFromJar(String outFileName, String libName) {
         try  {
             return  extractAndLoad(outFileName, libName);
        }  catch  (Throwable e) {
        }
         return   false ;
    }

     private   static   boolean  extractAndLoad(String outFileName, String libName)
             throws  Throwable {
         int  read;
         byte [] buf  =   new   byte [ 4096 ];
        InputStream is  =   null ;
        FileOutputStream os  =   null ;

        File file  =   new  File(outFileName);
         if  (file.exists())
            file.delete();

         if  ( ! file.exists()) {
             try  {
                os  =   new  FileOutputStream(file);
                is  =  Library. class .getResourceAsStream( " / "   +  libName);
                 if  (is  ==   null   ||  os  ==   null )
                     return   false ;

                 while  ((read  =  is.read(buf))  !=   - 1 )
                    os.write(buf,  0 , read);
            }  finally  {
                 if  (os  !=   null )
                    os.close();
                 if  (is  !=   null )
                    is.close();
            }

             if  ( ! SWT.getPlatform().equals( " win32 " )) {
                Runtime.getRuntime().exec(
                         new  String[] {  " chmod " ,  " 755 " , outFileName }).waitFor();
            }

             return  _load(outFileName);
        }

         return   false ;
    }
}

然后把您的原生库打在Jar里面,用Library.loadLibrary加载原生库,而不是System.loadLibrary,这样您就
把原生库隐藏在您的Jar里面了。

转载请保留http://www.blogjava.net/xilaile/archive/2007/03/23/105706.html

posted on 2007-03-23 02:10 gr8vyguy 阅读(5445) 评论(1)  编辑  收藏 所属分类: Java

评论

# re: 分析SWT3.3加载原生库的过程 2007-03-23 02:51 BeanSoft

就是先解压在系统临时目录里了, 然后再加载. 有时候会有问题, 例如你的 dll 需要相对路径的时候就不能用这种方法了, 倒是可以解压缩在一个固定的相对位置, 退出时清空.

不过 SWT 这个新特性的确是人性化的多了... 做软件多考虑用户, 是王道啊...  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 
<2007年3月>
25262728123
45678910
11121314151617
18192021222324
25262728293031
1234567

导航

统计

公告

  • 转载请注明出处.
  • msn: gr8vyguy at live.com
  • 常用链接

    留言簿(9)

    随笔分类(68)

    随笔档案(80)

    文章分类(1)

    My Open Source Projects

    搜索

    积分与排名

    最新评论