封宇's profile人脉和技术支撑一切BlogListsNetwork Tools Help

封宇

Occupation
Location
Interests
其实我是一个程序员,当然我很想成为架构师。当程序员真的很累啊。

人脉和技术支撑一切

殊途同归
October 20

感觉不能留在计算所了

很久没更新了,最近项目忙.
今天抽点时间来写一笔:计算所看来是没机会留下去了,得赶紧找工作才是
September 04

JNI调用C++从零开始

网上关于JNI C++调用的例子已经很多,但是感觉有些地方还是写得不够细,像我这种比较笨的人照着做还是碰到一些问题。后来好不容易解决了,决定把自己的整个流程记录下来,大家一起分享:)

 

项目中要用java调用c++写的算法类,折腾了好一段时间才勉强摸着点门道。

 

JNI是什么.JNI的完全名称是Java Native Interface (JNI),翻译过来就是java本地接口,用来实现javaVC++BCBVB...之间
进行调用,传值。对它的更多了解可以参看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文件放在里边的任何一个路径就可以找到了:)

 

好了,我们最简单的程序就算做完了。接下来处理一下有packagejni问题。

 

现在我们的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了,因为有些dllvs2003编译的),然后通通把他们拷贝到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)
  throws PortalException, SystemException {

  userId = userId.trim().toLowerCase();

  User user = UserUtil.findByPrimaryKey(userId);

  if (getUserId().equals(userId) ||
   hasAdministrator(user.getCompanyId())) {

   return user;
  }
  else {
   return (User)user.getProtected();
  }
 }

然后再看UserUtil.java749

protected static com.liferay.portal.model.User findByPrimaryKey(
  java.lang.String userId)
  throws com.liferay.portal.NoSuchUserException,
   com.liferay.portal.SystemException {
  UserPersistence persistence = (UserPersistence)InstancePool.get(PERSISTENCE);

  return persistence.findByPrimaryKey(userId);
 }

再找UserPersistence.java3026

protected com.liferay.portal.model.User findByPrimaryKey(String userId)
  throws NoSuchUserException, SystemException {
  com.liferay.portal.model.User user = UserPool.get(userId);
  Session session = null;

  try {
   if (user == null) {
    session = openSession();

    UserHBM userHBM = (UserHBM)session.load(UserHBM.class, userId);
    user = UserHBMUtil.model(userHBM);
   }

   return user;
  }
  catch (HibernateException he) {
   if (he instanceof ObjectNotFoundException) {
    throw new NoSuchUserException(userId.toString());
   }
   else {
    throw new SystemException(he);
   }
  }
  finally {
   HibernateUtil.closeSession(session);
  }
 }

看完上面这些代码是不是就明白Portal是怎么通过userId得到User对象的?上面一段代码中红色的那句用过hibernate的人都会很熟悉,通过主键得到对象。

我们再深入一下,看一下BasePersistence.java,这个是UserPersistence.java的父类,上面代码中openSession()在这个类里定义。

public class BasePersistence {

 protected Session openSession() throws HibernateException {
  return HibernateUtil.openSession(getHibernateConfigurationClassName());
 }

 protected Dialect getDialect() {
  return HibernateUtil.getDialect(getHibernateConfigurationClassName());
 }

 protected String getHibernateConfigurationClassName() {
  return HibernateConfiguration.class.getName();
 }

}

再看com.liferay.portal.util.HibernateUtil.java63

public static Session openSession(String className)
  throws HibernateException {

  SessionConfiguration config =
   _getSessionConfigurationInstance(className);

  return config.openSession();
 }


com.liferay.util.dao.hibernate.SessionConfiguration.java是一个抽象类,让我直接看其子类com.liferay.portal.util.HibernateConfiguration.java

public class HibernateConfiguration extends SessionConfiguration {

 public void init() {
  try {
   ClassLoader classLoader = getClass().getClassLoader();

   Configuration cfg = new Configuration();

   String[] configs = StringUtil.split(
    SystemProperties.get("hibernate.configs"));

   for (int i = 0; i < configs.length; i++) {
    try {
     InputStream is =
      classLoader.getResourceAsStream(configs[i]);

     if (is != null) {
      cfg = cfg.addInputStream(is);

      is.close();
     }
    }
    catch (Exception e) {
     e.printStackTrace();
    }
   }

   cfg.setProperties(SystemProperties.getProperties());

   setSessionFactory(cfg.buildSessionFactory());
  }
  catch (Exception e) {
   e.printStackTrace();
  }
 }

}


上面代码中的红色部分是从系统的properties文件中读取一些值,实际上是调用portal-ejb/system.properties文件,从这个文件的第94行就可以发现一些Hibernate用来做O/R mapping的文件。代码中通过InputStream方式把这些mapping files加到Configuration中去。

大家可以去看一下这些mapping files是不是我们所熟悉的****.hbm.xml的格式?

后记:

读了一早上的源码,写了一个小时的blog,无非是想切实的解决一个问题。通过读源码可以了解一些机制,也可以了解一些设计思想,对于每一个学习者都是有益的。从开始不知道用什么方法好,就去找admin portlet里找JSPlist-users.jsp),里面有一些相关的用户管理的方法(User[] users = (User[])CompanyLocalManagerUtil.getUsers(company.getCompanyId()).toArray(new User[0]);),从中找到一个得到所有User的方法,也让我联想的去找一些相关的类,后来我就找到UserManagerUtil.java。这个过程是猜测到证实的过程,当你的猜测得到证实的时候,当你发现那个经过多层包装的你熟悉的接口方法的时候,那种感觉是快乐的,满足的。