| 封宇's profile人脉和技术支撑一切BlogListsNetwork | Help |
|
人脉和技术支撑一切殊途同归 September 04 JNI调用C++从零开始网上关于JNI C++调用的例子已经很多,但是感觉有些地方还是写得不够细,像我这种比较笨的人照着做还是碰到一些问题。后来好不容易解决了,决定把自己的整个流程记录下来,大家一起分享:)
项目中要用java调用c++写的算法类,折腾了好一段时间才勉强摸着点门道。
JNI是什么.JNI的完全名称是Java Native Interface (JNI),翻译过来就是java本地接口,用来实现java与VC++或BCB或VB...之间 进行调用,传值。对它的更多了解可以参看java文档。 我们即将开始一个最简单的例子,开始之前先介绍一下我用的开发环境。JDK1.4.2,一般来说应该在同一个JDK版本下完成开发和运行(不同版本交叉使用会不会有问题我没试过)。Eclipe3.2+myeclipse5.0,这个组合还算经典吧,不过具体的开发工具并没有多大的影响。Visual Studio.NET 2005,这个用来写c++的程序。
首先在eclipse中建立一个java工程StatisticJNIProject,设置好必要的参数(主要是输出目录,好找到你编译后的class文件位置:-)),在根目录(一般是src目录,注意是根目录,也就是程序中没有package,原因后边再说)下建立SITStatistic.java文件(当然,文件名可以随便取了)。文件内容如下:
/* * Created on 2006-8-22 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */
public class SITStatistic { static { System.loadLibrary("Tjni"); }
public native String doStatistic(String requestXML, String statisticPattern);
public static void main(String[] args) {
String xml = "This is my test string."; SITStatistic hh = new SITStatistic(); String r = ""; try { r = hh.doStatistic(xml, "fg"); } catch (Exception e) { System.out.println(e); } System.out.println("result = " + r); //System.getProperties(); //System.out.println(System.getProperties()); }
}
接下来编译工程,会得到SITStatistic.class文件(如果不用开发环境也可以直接用javac命令便宜文件SITStatistic.java,反正目标是生成SITStatistic.class文件)。 启动命令行(开始->运行 cmd.exe),将当前目录转到SITStatistic.class所在目录,执行javah SITStatistic 命令(注意,不是javah SITStatistic.class,没有扩展名),系统将在当前目录下生成SITStatistic.h文件。这个文件正是c++需要实现的头文件。 到此,这个简单的例子的java部分已经全部实现完成了。我们接下来转到c++部分。
首先在%JAVA_HOME%/include文件夹及其子文件夹下找到jni.h,jawt_md.h,jni_md.h三个文件,将他们拷贝到 Visual Studio安装目录\VC\include 目录下(其他的开发环境只要能保证编译是能找到这写头文件即可)。 建立vc的工程 new->project 点Visual C++下的Win32,选择Win32 Project。填写项目名称TJni,点OK。在Application Settings中设置Application Type 为DLL,Application options 为Empty project,点OK,工程就算建好了。说了一堆,其实就是建一个基本的动态链接库工程。然后把我们之前生成的SITStatistic.h文件加入到Tjni工程中,并新添加一个文件SITStatistic.java实现SITStatistic.h定义的函数,具体代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class SITStatistic */
#ifndef _Included_SITStatistic #define _Included_SITStatistic #ifdef __cplusplus extern "C" { #endif /* * Class: SITStatistic * Method: doStatistic * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_SITStatistic_doStatistic (JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus } #endif #endif
生成工程,会得到TJni.dll文件。把该文件考到SITStatistic.class文件所在的文件夹,执行SITStatistic.class文件,有可能你就成功了:)(卖个关子)。 我在eclipse里边运行时其实并没有成功。后来发现是路径问题。错误如下: java.lang.UnsatisfiedLinkError: no Tjni in java.library.path at java.lang.ClassLoader.loadLibrary(Unknown Source) at java.lang.Runtime.loadLibrary0(Unknown Source) at java.lang.System.loadLibrary(Unknown Source) at SITStatistic.<clinit>(SITStatistic.java:16) Exception in thread "main" 系统找不到路径,我猜想在elipse里运行时,系统路径可能并不是程序class文件所在的路径。把TJni.dll放到%系统所在驱动器%\WINDOWS里就可以了。当然,比较好的做法法是用System.out.println(System.getProperties());把java.library.path 输出来,只要把TJni.dll文件放在里边的任何一个路径就可以找到了:)
好了,我们最简单的程序就算做完了。接下来处理一下有package的jni问题。
现在我们的SITStatistic.java文件在E:\Work-Space\forICT\src\StatisticJNIProject\src\com\ict\bpf\ext\statistic\jni这个位置,编译后的class文件在E:\Work-Space\forICT\bin\StatisticJNIProject\src\com\ict\bpf\ext\statistic\jni。显然,包为com.ict.bpf.ext.statistic.jni,如果还按照没有包的方法编译头文件,最后java程序会报找不到路径的问题,错误如下: java.lang.UnsatisfiedLinkError: doStatistic at SITStatistic.doStatistic(Native Method) at SITStatistic.main(SITStatistic.java:27) Exception in thread "main" 注意区别找不到dll是的错误,两者是不同的:)。 解决这个问题的方法很简单,我们在E:\Work-Space\forICT\src\StatisticJNIProject\bin目录下用控制台执行javah com.ict.bpf.ext.statistic.jni.SITStatistic即可得到对应的SITStatistic.h文件,内容如下: /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class SITStatistic */
#ifndef _Included_SITStatistic #define _Included_SITStatistic #ifdef __cplusplus extern "C" { #endif /* * Class: SITStatistic * Method: doStatistic * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_ict_bpf_ext_statistic_jni_SITStatistic_doStatistic (JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus } #endif #endif
注意JNIEXPORT jstring JNICALL Java_com_ict_bpf_ext_statistic_jni_SITStatistic_doStatistic这句,这是该文件和不带package的那个头文件的唯一区别所在,它指明了package的路径。后边的开发都和不带package的过程一致了。 到此,jni调用也差不多算是入门了,当然深入下去这个东西还是很有内容的哦:)。 实际的项目中我开发的dll还依赖于其他的dll文件,我把这些dll文件和我开发生成的dll文件放在一个目录下,在vs2005下编译通过(后又改为vs2003了,因为有些dll是vs2003编译的),然后通通把他们拷贝到java.library.path(之前已经提过)的路径里,一切OK?慢着,有时候运行时系统提示缺个M打头的dll文件(具体文件名我忘了)。不用着急,这估计是环境问题,因为有些dll不是在我本机编译的。到网上google或者baidu一下,把缺的文件找回来装上,再运行,一切OK。 抽了两天的间隙,终于把这篇文章写完了。希望能对大家有所帮助。 封宇 2006.9.4 August 30 MyEclipse 5.0的破戒算法源程序 今天终于把elipse从3.0升级到3.2了.之所以升级主要是发现3.0有bug了(有些xml文档明明正确,它却要报错),而且3.0太老了,对spring,web service等的支持太差了.虽然舍不得换(已经用3.0很久了),但是工欲善其事,必先利其器.一咬牙换了.
换是换了,当然要给它配个MyEclipse了.鄙人没米,单位也穷.隧满互联网找破解程序.上天抬爱啊,找到一个极其简陋的破解程序,用java写的,才2K大小,带源码.试用了一下,嘿!还真管用.于是很佩服写程序的人.的确是很牛啊.
不敢独自享用,所以把代码贴出来大家分享:
/**************************************************************************************/
/* 以下是源代码 */
/**************************************************************************************/
import java.io.*;
/**
* * modify by TOTO * totongf@hotmail.com * 2005-10-24 */ public class Crack { public static String convert(String s) { if (s == null || s.length() == 0) return s; byte abyte0[] = s.getBytes(); char ac[] = new char[s.length()]; int i = 0; for (int k = abyte0.length; i < k; i++) { int j = abyte0[i]; if (j >= 48 && j <= 57) j = ((j - 48) + 5) % 10 + 48; else if (j >= 65 && j <= 90) j = ((j - 65) + 13) % 26 + 65; else if (j >= 97 && j <= 122) j = ((j - 97) + 13) % 26 + 97; ac[i] = (char) j; } return String.valueOf(ac); } private static int hash(String s) {
int i = 0; char ac[] = s.toCharArray(); int j = 0; for (int k = ac.length; j < k; j++) i = 31 * i + ac[j]; return Math.abs(i); } private static String inputString() {
BufferedReader bufferedreader = new BufferedReader( new InputStreamReader(System.in)); String s = null; try { s = bufferedreader.readLine(); } catch (IOException ioexception) { ioexception.printStackTrace(); } return s; } public static void main(String args[]) {
try { System.out.println("My Eclipse IDE v9.99 Keygen"); System.out.print("License Name : "); String s = inputString(); String licStr = "YE3MP-999-00-9912310"; String h = s.substring(0, 1) + licStr + "Decompiling this copyrighted software is a violation of both your license agreement and the Digital Millenium Copyright Act of 1998 (http://www.loc.gov/copyright/legislation/dmca.pdf). Under section 1204 of the DMCA, penalties range up to a $500,000 fine or up to five years imprisonment for a first offense. Think about it; pay for a license, avoid prosecution, and feel better about yourself." + s; int j = hash(h); String lic = s.substring(0, 1) + licStr + Integer.toString(j);
System.out.println("License Key : " + convert(lic)); } catch (Exception exception) { exception.printStackTrace(); } } } August 22 初探Liferay Enterpriese Portal的数据类型持久化前言: 最近在忙的一个项目是建立在Liferay Enterprise Portal上的,设计的几张数据表要与已有的Portal数据库中的User_表有级联关系。之前从Liferay的官方网站上了解到Liferay的Portal持久化都是通过Hibernate来实现,故对其实现的方法产生了小小的兴趣(因为我的数据表也打算通过Hibernate的O/R映射来实现,代码中难免要使用Portal的类com.liferay.portal.model.User)。
正文: 其实了解各种实现的方法,最好的莫过于读源码。(只要你有这个耐心) 我开始只想通过userId得到User对象,关于User对象的各种相关的类都在包com.liferay.portal.ejb里,你可以看到User开头的类有一堆,呵呵,其实我们这里只要涉及几个重要的类: UserHBM.java 这个是User类的一个翻版,里面的信息和User里的信息基本一致(没仔细看),Portal也是通过这个类来映射数据库的User_表的。 UserPool.java 这个是一个与Cache相关的类,可不关心,并不影响理解。 UserManager.java 定义了与User相关的一组接口,对一些重要属性如Group,Role等的添加删除等。 UserManagerImpl.java 实现了UserManager接口,并继承了PrincipalBean.java。 UserUtil.java 个人觉得有点象实体bean的变体,里面有create,remove,update等方法,还有一些查找方法。 UserPersistence.java 这个里面是具体数据库的实现方法,UserUtil都是调用这个类的方法。 让我们先看看UserManagerImpl.java中关于得到User的代码,源码第400行
public User getUserById(String userId) userId = userId.trim().toLowerCase(); User user = UserUtil.findByPrimaryKey(userId); if (getUserId().equals(userId) || return user;
然后再看UserUtil.java的749行
protected static com.liferay.portal.model.User findByPrimaryKey( return persistence.findByPrimaryKey(userId);
再找UserPersistence.java的3026行
protected com.liferay.portal.model.User findByPrimaryKey(String userId) try { UserHBM userHBM = (UserHBM)session.load(UserHBM.class, userId); return user;
看完上面这些代码是不是就明白Portal是怎么通过userId得到User对象的?上面一段代码中红色的那句用过hibernate的人都会很熟悉,通过主键得到对象。 我们再深入一下,看一下BasePersistence.java,这个是UserPersistence.java的父类,上面代码中openSession()在这个类里定义。
public class BasePersistence { protected Session openSession() throws HibernateException { protected Dialect getDialect() { protected String getHibernateConfigurationClassName() { }
再看com.liferay.portal.util.HibernateUtil.java的63行
public static Session openSession(String className) SessionConfiguration config = return config.openSession();
public class HibernateConfiguration extends SessionConfiguration { public void init() { Configuration cfg = new Configuration(); String[] configs = StringUtil.split( for (int i = 0; i < configs.length; i++) { if (is != null) { is.close(); cfg.setProperties(SystemProperties.getProperties()); setSessionFactory(cfg.buildSessionFactory()); }
大家可以去看一下这些mapping files是不是我们所熟悉的****.hbm.xml的格式? 后记: 读了一早上的源码,写了一个小时的blog,无非是想切实的解决一个问题。通过读源码可以了解一些机制,也可以了解一些设计思想,对于每一个学习者都是有益的。从开始不知道用什么方法好,就去找admin portlet里找JSP(list-users.jsp),里面有一些相关的用户管理的方法(User[] users = (User[])CompanyLocalManagerUtil.getUsers(company.getCompanyId()).toArray(new User[0]);),从中找到一个得到所有User的方法,也让我联想的去找一些相关的类,后来我就找到UserManagerUtil.java。这个过程是猜测到证实的过程,当你的猜测得到证实的时候,当你发现那个经过多层包装的你熟悉的接口方法的时候,那种感觉是快乐的,满足的。 |
|||
|
|