注:新版本NLPIR的JNI接口支持配置,可以将链接库和Data目录定义到配置文件中,但好像不支持UTF-8编码。


由于项目中要应用分词,因此前几天的时候了解了一下中文分词系统NLPIR(又叫ICTCLAS2013)的应用,并写了一篇学习笔记:


前面只是记录了一下在普通的Java工程中如何使用NLPIR,相对来说很简单,使用起来比较容易;但我们的项目是Web项目,因此从前天开始试着将NLPIR整合到Web工程下。还真是遇到了麻烦,主要也是因为第一次使用JNI,对其不够熟悉,折腾了许久才成功运行分词示例,这里记录一下。

起初以为跟普通项目一样,等我把东西都拷到项目中运行示例程序时,报了一个异常:

Caused by: java.lang.UnsatisfiedLinkError: no NLPIR_JNI in java.library.path
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1738)
	at java.lang.Runtime.loadLibrary0(Runtime.java:823)
	at java.lang.System.loadLibrary(System.java:1028)
	at kevin.zhang.NLPIR.<clinit>(NLPIR.java:132)



说是异常,其实是个Error。很明显,程序无法加载NLPIR_JNI.dll动态链接库(起初我想当然地将它们放在了项目的WebRoot下)。这个好办,不就是找不到库文件吗?我挪到你能找到的地方还不行吗!!!挪到哪呢?我又想当然地将dll文件放到src下,不成功!与NLPIR.java放一个包中,仍然不成功!!

摆弄了很久都是UnsatisfiedLinkError,突然看到了“no NLPIR_JNI in java.library.path”这句话。不是说在java.library.path 中找不到吗?我把库文件移到 java.library.path 中总没什么可说的了吧!然后我就在Web项目中将 java.library.path 这个系统属性输出了一下,结果出来两个路径:

  • %JAVA_HOME%/bin
  • %TOMCAT_HOME%/bin

这么说java.library.path系统属性在Windows系统下由两个路径组成,第一个是JDK安装目录下的bin目录;第二个是Tomcat安装目录下的bin目录(我用的服务器是Tomcat,其它的服务器不确定是什么情况,请自行测试)。

这下就好办了,直接将 NLPIR.dll 和 NLPIR_JNI.dll 拷到这两个路径中的其中一个,运行,成功加载!!!

如果我们跟着代码去JDK源码查看一下,最终会追踪到ClassLoader的loadLibrary方法中,其代码如下(整理过):

static void loadLibrary(Class fromClass, String name, boolean isAbsolute) {
	// 一些其它代码
	ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
	if (sys_paths == null) {
		usr_paths = initializePath("java.library.path");
		sys_paths = initializePath("sun.boot.library.path");
	}
	if (isAbsolute) {
		if (loadLibrary0(fromClass, new File(name))) {
			return;
		}
		throw new UnsatisfiedLinkError("Can't load library: " + name);
	}
	// 删除一些代码
	for (int i = 0 ; i < sys_paths.length ; i++) {
	    File libfile = new File(sys_paths[i], System.mapLibraryName(name));
	    if (loadLibrary0(fromClass, libfile)) {
	        return;
	    }
	}
	if (loader != null) {
	    for (int i = 0 ; i < usr_paths.length ; i++) {
	        File libfile = new File(usr_paths[i], System.mapLibraryName(name));
			if (loadLibrary0(fromClass, libfile)) {
				return;
			}
	    }
	}
	// Oops, it failed
    throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
}



从上面的代码可以看到,两个String数组类型的成员变量对加载库文件有关:sys_paths 和 usr_paths,在这里打个断点就会发现,usr_paths 中就是我们在上面用System.getProperty("java.library.path") 获取的的两个路径,而sys_paths中有一个成员:%JRE_HOME%/bin,所以如果我们将库文件放在此处,应该也能成功加载(经过测试,确实如此)。

综上所述,在Windows系统上的Web项目中应用NLPIR分词系统至少有三个地方可以放置我们的dll文件(在本人机器环境上如此,不保证所有Windows系统都是如此,请自行测试):

  • %JAVA_HOME%/bin
  • %TOMCAT_HOME%/bin
  • %JRE_HOME%/bin

在我的机器上就是:C:\Java\jdk1.6.0_45\bin 、 C:\Java\jdk1.6.0_45\jre\bin 和 E:\servers\apache-tomcat-6.0.37\bin 三个路径。

另外,JNI接口NLPIR静态块中的加载也可以换成System.load()方式,这里需要传递加载库的绝对路径,暂时没有测试!!!


Linux系统上Web项目应用NLPIR分词


我的开发环境是Windows,但项目最终会部署到Linux系统上,于是今天下午又在Linux上调试了一下,有了前面的经验,Linux上就轻车熟路了。项目不需改动,需要改动的就是NLPIR的库文件了。

Linux环境所需要的库文件是 libNLPIR.so 和 libNLPIR_JNI.so 两个(由于Linux系统和Linux上的JDK都是64位的,所以我下载的是64位的库),其它所需Data目录和Java接口与Windows一样。

首先在Linux上查看了一下 java.library.path 所包含的路径,结果如下所示:

opennlp 中文分词模型 nlpir汉语分词系统_JAVA

于是我将两个库文件放到了 /usr/local/lib 目录下,启动Tomcat,访问,结果证明这样做是可以的!!!

按理说,上面图片中列出来的所有路径都应该可以的,但我没有挨个测试。


注:在Web项目中,NLPIR.NLPIR_Init初始化方法中的路径参数用的是绝对路径。