Java Votary

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  48 随笔 :: 1 文章 :: 80 评论 :: 0 Trackbacks

2005年12月31日 #

ThreadLocal是什么

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

线程局部变量并不是Java的新发明,很多语言(如IBM IBM XL FORTRAN)在语法层面就提供线程局部变量。在Java中没有提供在语言级支持,而是变相地通过ThreadLocal的类提供支持。

所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。

ThreadLocal的接口方法

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

  • void set(Object value)

设置当前线程的线程局部变量的值。

  • public Object get()

该方法返回当前线程所对应的线程局部变量。

  • public void remove()

将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

  • protected Object initialValue()

返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法 也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:

代码清单1 SimpleThreadLocal

public class SimpleThreadLocal {

private Map valueMap = Collections.synchronizedMap(new HashMap());

public void set(Object newValue) {

valueMap.put(Thread.currentThread(), newValue);①键为线程对象,值为本线程的变量副本

}

public Object get() {

Thread currentThread = Thread.currentThread();

Object o = valueMap.get(currentThread);②返回本线程对应的变量

if (o == null && !valueMap.containsKey(currentThread)) {③如果在Map中不存在,放到Map

中保存起来。

o = initialValue();

valueMap.put(currentThread, o);

}

return o;

}

public void remove() {

valueMap.remove(Thread.currentThread());

}

public Object initialValue() {

return null;

}

}

虽然代码清单9‑3这个ThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是相近的。

一个TheadLocal实例

下面,我们通过一个具体的实例了解一下ThreadLocal的具体使用方法。

代码清单2 SequenceNumber

package com.baobaotao.basic;

public class SequenceNumber {

通过匿名内部类覆盖ThreadLocalinitialValue()方法,指定初始值

private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){

public Integer initialValue(){

return 0;

}

};

获取下一个序列值

public int getNextNum(){

seqNum.set(seqNum.get()+1);

return seqNum.get();

}

public static void main(String[] args)

{

SequenceNumber sn = new SequenceNumber();

3个线程共享sn,各自产生序列号

TestClient t1 = new TestClient(sn);

TestClient t2 = new TestClient(sn);

TestClient t3 = new TestClient(sn);

t1.start();

t2.start();

t3.start();

}

private static class TestClient extends Thread

{

private SequenceNumber sn;

public TestClient(SequenceNumber sn) {

this.sn = sn;

}

public void run()

{

for (int i = 0; i < 3; i++) {④每个线程打出3个序列值

System.out.println("thread["+Thread.currentThread().getName()+

"] sn["+sn.getNextNum()+"]");

}

}

}

}

 

通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号, 在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:

thread[Thread-2] sn[1]

thread[Thread-0] sn[1]

thread[Thread-1] sn[1]

thread[Thread-2] sn[2]

thread[Thread-0] sn[2]

thread[Thread-1] sn[2]

thread[Thread-2] sn[3]

thread[Thread-0] sn[3]

thread[Thread-1] sn[3]

考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

Thread同步机制的比较

ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线 程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编 写多线程代码时,可以把不安全的变量封装进ThreadLocal。

由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用,代码清单 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。

概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

Spring使用ThreadLocal解决线程安全问题

我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用 域。就是因为Spring对一些Bean(如RequestContextHolder、 TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用 ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9‑2所示:

通通透透理解ThreadLocal

1同一线程贯通三层

这样你就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。

下面的实例能够体现Spring对有状态Bean的改造思路:

代码清单3 TopicDao:非线程安全

public class TopicDao {

private Connection conn;一个非线程安全的变量

public void addTopic(){

Statement stat = conn.createStatement();引用非线程安全变量

}

}

由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:

代码清单4 TopicDao:线程安全

import java.sql.Connection;

import java.sql.Statement;

public class TopicDao {

①使用ThreadLocal保存Connection变量

private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();

public static Connection getConnection(){

②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,

并将其保存到线程本地变量中。

if (connThreadLocal.get() == null) {

Connection conn = ConnectionManager.getConnection();

connThreadLocal.set(conn);

return conn;

}else{

return connThreadLocal.get();③直接返回线程本地变量

}

}

public void addTopic() {

④从ThreadLocal中获取线程对应的Connection

Statement stat = getConnection().createStatement();

}

}

不同的线程在使用TopicDao时,先判断connThreadLocal.get()是否是null,如果是null,则说明当前线程还没有对 应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了 Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其它线程的 Connection。因此,这个TopicDao就可以做到singleton共享了。

当然,这个例子本身很粗糙,将Connection的ThreadLocal直接放在DAO只能做到本DAO的多个方法共享Connection时 不发生线程安全问题,但无法和其它DAO共用同一个Connection,要做到同一事务多DAO共享同一Connection,必须在一个共同的外部类 使用ThreadLocal保存Connection。

小结

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况 下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

posted @ 2009-07-01 12:15 Dion 阅读(335) | 评论 (0)编辑 收藏

     摘要: 题记世界是事实的总体,而不是事物的总体。世界为诸事实所规定,为它们既是全部事实所规定。——路德维希·维特根斯坦,《逻辑哲学论》The answer, please?"请回答我..."The stern voice startles you. 一个严厉的声音把你吓个半死.You were dozing in Mrs. Rosencrantz's high school math class agai...  阅读全文
posted @ 2006-03-11 10:02 Dion 阅读(2096) | 评论 (0)编辑 收藏

前几天跟着写了一个简单的例子.
觉得Drools的配置也没有什么.
今天在运行house的例子的时候, 无论怎么样, 总是异常: 没有定义的SMF.
显然没有找到我定义的drools.config文件.
官方网站上是这样写地:
String droolsConfigProp = System.getProperty( "drools.conf" );

if ( droolsConfigProp != null )
{
    loadConfig( droolsConfigProp );
}

ClassLoader cl = Thread.currentThread( ).getContextClassLoader( ); if ( cl == null )
{
    cl = getClass( ).getClassLoader( );
}

Enumeration configUrls = cl.getResources( "META-INF/drools.conf" );

if ( !configUrls.hasMoreElements( ) )
{
    cl = getClass( ).getClassLoader( );
    configUrls = cl.getResources( "META-INF/drools.conf" );
}

if ( !configUrls.hasMoreElements( ) )
{
    cl = ClassLoader.getSystemClassLoader( );
    configUrls = cl.getResources( "META-INF/drools.conf" );
}

this.classLoader = cl;
while ( configUrls.hasMoreElements( ) )
{
    URL configUrl = (URL) configUrls.nextElement( );
    loadConfig( configUrl );
}

好像每一个旮旯里面都找了, 为什么没有找到我的呢?
System.getProperty指向的位置并不一定和loadFromUrl位置一样.呵呵.
posted @ 2006-03-11 10:00 Dion 阅读(1631) | 评论 (0)编辑 收藏

内容提要
       在本文的第一部分,我将讨论规则引擎如何帮助你从软件的应用逻辑中分离出商业规则逻辑,以实现商业应用的灵活性。另外,我还将介绍JSR-94规则引擎 API,及其开源实现Drools项目,它是这一新技术的先驱。在第二部分,我们将介绍一个规则引擎例子,并深入地研究Drools引擎及其JSR-94 扩展的复杂性。

为什么使用规则引擎
       商业世界充满了关于变化的陈词滥调,如任何事物都会改变,唯一不变的是变化等等。而在技术领域里,情况正好相反。我们仍然在试图解决30年前软件业中同样 的一堆问题--也许比30年前还要多的问题。在过去的十年,IT从业人员淹没在软件方法学的大量文献中,如快速软件开发,极限编程,敏捷软件开发等,它们 无一例外地强调灵活和变化的重要性。
       但商业通常比开发团队所依赖的软件过程和技术改变得更加迅速。当商业策划人员试图重整IT部门,以支持新的业务转型时,仍然觉得很费劲。

Lost in Translation
       虽然IT团队反应迅速,但他们通常带来"电话效应"――IT给商业计划的执行带来的阻力和它带来的利益一样多。不幸的是,在开发团队完全理解商业决策规则 并实现之前,规则已经改变了。在软件进入市场前,它已经过时了,需要进行重构以满足新的业务需求。如果你是一个开发人员,你会知道我在说什么。再也没有比 在需求变动的情况下构造软件让开发人员更沮丧的事情了。作为软件开发人员,你必须比业务人员更了解业务,有时还要了解更多。
       试想一下你是一位商业决策者。假如公司的成功依赖于你对于市场趋势敏锐的洞察力,它常常帮助你领先于竞争者利用变化的市场环境获利。每天你都会得到更多更 好的市场信息,但并不要紧。完成新产品开发可能需要6-9个月,在此期间,对于市场大胆和敏锐的洞察和信息优势可能已经浪费了。而且,当产品发布时,有这 样几种可能:产品没有什么吸引人的特性,预算超支,过了产品的最佳发布期限,或三者兼而有之。
       情况可能还会更糟,在完成产品开发时,市场环境和规划产品开发时相比,已经发生了根本变化。现在你必须要遵守新的规则,你已经丧失了你的边际优势,而且设 计软件的五人中的三人已经离开了公司。你必须给接手的新人重新讲解复杂的业务。如果事情不顺利,你可能发现自己要对付一个缺少文档,并且你完全不了解的遗 留应用。
       你的战略在哪出现了问题?你在哪里应该可以做到更好?最近的轻量级软件过程,如极限编程,敏捷软件开发等都在强调自动单元测试和软件功能优先级的重要性。 除此之外,还有其他的原则,你的开发团队可能也很熟悉,这些原则可以帮助他们对需求的变动作出迅速反应并缩短项目的开发周期。这些原则的大多数,如系统分 解,多年前就已经出现,并得到了Java平台的支持(如JMX等),还有如面向对象和角色建模,已经内建在Java语言中。
       但Java仍然是一门相当年轻的语言,而且Java平台远远还没有完备。当前在Java社区,一个引人注目的新技术是,分离商业决策者的商业决策逻辑和应 用开发者的技术决策,并把这些商业决策放在中心数据库,让它们能在运行时(即商务时间)可以动态地管理和修改。这是一个你值得考虑的策略。
       为什么你的开发团队不得不象商业经理人一样,在代码中包含复杂微妙的商业决策逻辑呢?你怎样才能向他们解释决策推理的微妙之处呢?你这样做是否谨慎呢?可 能不是。象bottom line一样,某些东西在解释的过程中丢失了。为什么要冒这样的风险,让应用代码或测试代码错误地表达你的商业决策逻辑呢?如果这样做的话,你怎样检查它 们的正确性呢――难道你自己想学习如何编程和编写测试代码,或者你的客户会为你测试软件?你一方面要应付市场,一方面要应付软件代码,这实在太困难了。
       如果能将这些商业决策规则集中地放在一个地方,以一种你可以理解的格式定义,让你可以直接管理,而不是散落在代码的各个角落,那该有多好。如果你能把商业 决策规则独立于你的软件代码,让开发团队作出技术决策,你将会获得更多好处。你的项目开发周期会更短,软件对于变动的需求更灵活。

规则引擎标准Java API
       2003年11月,Java社区通过了Java Rule Engine API规范(JSR-94)的最后草案。这个新的API让开发人员在运行时访问和执行规则有了统一的标准方式。随着新规范产品实现的成熟和推向市场,开发 团队将可以从应用代码中抽取出商业决策逻辑。
       这就需要新一代的管理工具,帮助商务经理人可以定义和细化软件系统的行为。不必通过开发过程来修改应用,并假定可以得到正确的结果,经理人将可以随时根据需要修改决策规则,并进行测试。
       但这将需要开发人员在设计系统时作出某些改变,并可以得到合适的开发工具。

分离商务和技术的关注点
       这是一个非常简单的例子,从经理人的角度,说明如何分离商务和技术的关注点。
       你管理着一个反向投资基金。你公司计算机系统的一部分用于分析股票价格,收益和每股净资产,并在需要时向你提出预警。这个计算机系统的工作是,识别出PE比率比市场平均值低的股票,并标记出来以便进一步的检查。
       你的IT部门拥有一大堆数据,并开发了一系列你可以在规则中引用的简单数据对象。现在,为简单起见,假设你是一名受过良好教育的,了解技术的管理人,你了解XML的基本知识,可以让你编写和修改简单的XML规则文件。
       你的第一个规则是,给道琼斯所有的股票估值,并剔除P/E比率大于10的股票(这有点过分简化,但这里只作为一个例子)。保留下来的股票用来生产一系列报表。对于这个简单的例子,你的规则文件看起来如下(我们将会过头来讨论这个文件的结构):

<stock:overvalued>
    <stock:index> DJIA </stock:index>
    <stock:pe> over 10.0 </stock:pe>
</stock:overvalued>

       一个月后,你接到一家巴西分析师公司的电话,雇佣你的公司生成一系列巴西股市的报表,但他们有更严格的标准。而目前在巴西,P/E比率市场平均值是个位 数,因此你用来评估被市场低股票的阈值需要改变。除了较低的P/E比率,你的新客户还要求以Price-to-Book比率作为参考标准。
       你启动规则编辑器,并修改规则以匹配新的评估条件。现在,规则引擎剔除巴西股市中P/E比率大于6.5,以及Price to Book 比率小于等于1的股票。完成规则文件修改后,看起来如下:

<stock:overvalued>
    <stock:index> Brazil </stock:index>
    <stock:pe> over 6.5 </stock:pe>
    <stock:pb> over 1.0 </stock:pb>
</stock:overvalued>

       你无需为此向开发团队作任何解释。你无需等待他们开发或测试程序。如果你的规则引擎的语义足够强大,让你描述工作数据,你可以随时按需修改商业规则。
       如果限制因素是规则的定义语言和数据模型,你可以确信这两者将会标准化,并出现先进的编辑器和工具,以简化规则的定义,保存和维护。
       现在,我希望你已经清楚以下的原则:在这个例子中,哪只股票是否被选择是一个商务决策,而不是技术决策。决定将哪只股票交给你的分析师是经理人的逻辑 ――"logic of the bottom line"。经理人作出这些决策,并可以按需定制应用。这些规则因此变成了一种控制界面,一种新的商业系统用户界面。

使用Rule开发
       如果在这个应用场景中,你是一个开发人员,你的工作会稍微轻松一些。一旦你拥有了一种用于分析股票的规则语言,你可以取出数据对象并交给规则引擎执行。我们将会到规则语言的讨论,但现在我们继续刚才的例子。
       你的系统将一系列的stock bean输入规则引擎。当规则执行后,你可以选出符合条件的股票并可以对它们作进一步处理。也许是把它们输入报表生成系统。分析师使用这些报表帮助他们分 析股市。同时,老板也可能让你使用新的技术分析工具,并用Dow理论预测股市的底部和顶部。
       规则引擎可以让你的系统变得更简单,因为你无需在代码中编写商务逻辑,如怎样选择股票,选择股票过程中奇怪的条件组合等。这些逻辑不再进入你的代码。你将可以专注于数据模型。
       现在可以这么认为,通过从应用代码中剥离出易变的商业逻辑,你的效率会更高。但凡是总有例外――简单应用可能并不能从规则系统中获益。但如果你开发一个大型系统,有很多易变的商业逻辑,你可以考虑在应用中集成规则引擎。
       除了从应用代码中剥离出商业决策逻辑外,规则引擎还有其他用处。有时候你需要应用成百上千的规则进行决策,并且有上千个对象和这些规则一起使用。很难想象 有什么先进的人工智能引擎可以处理这种情况。遇到这种情况,你需要一个极快的决策算法或是大型机。大型机并不便宜,但你可以非常便宜的得到效率和可伸缩性 最好的算法。

Bob McWhirter的Drools项目
       现在,我要介绍Drools项目,Charles Forgy Rete算法的一个增强的Java语言实现。Drools是一个Bob McWhirter开发的开源项目,放在The Codehaus上。在我写这篇文章时,Drools发表了2.0-beata-14版。在CVS中,已完整地实现了JSR94 Rule Engine API并提供了单元测试代码。
       Rete算法是Charles Forgy在1979年发明的,是目前用于生产系统的效率最高的算法(除了私有的Rete II)。Rete是唯一的,效率与执行规则数目无关的决策支持算法。For the uninitiated, that means it can scale to incorporate and execute hundreds of thousands of rules in a manner which is an order of magnitude more efficient then the next best algorithm。Rete应用于生产系统已经有很多年了,但在Java开源软件中并没有得到广泛应用(讨论Rete算法的文档参见http://herzberg.ca.sandia.gov/jess/docs/61/rete.html。)。
       除了应用了Rete核心算法,开源软件License和100%的Java实现之外,Drools还提供了很多有用的特性。其中包括实现了JSR94 API和创新的规则语义系统,这个语义系统可用来编写描述规则的语言。目前,Drools提供了三种语义模块――Python模块,Java模块和 Groovy模块。本文余下部分集中讨论JSR94 API,我将在第二篇文章中讨论语义系统。
       作为使用javax.rules API的开发人员,你的目标是构造一个RuleExecutionSet对象,并在运行时通过它获得一个RuleSession对象。为了简化这个过程, 我编写了一个规则引擎API的fa?ade,可以用来解释代表Drools的DRL文件的InputStream,并构造一个 RuleExecutionSet对象。
       在上面提到了Drools的三种语义模块,我接下来使用它们重新编写上面的例子XML规则文件。这个例子中我选择Java模块。使用Java模块重新编写的规则文件如下:

<rule-set name="StockFlagger"
      xmlns="http://drools.org/rules"
      xmlns:java="http://drools.org/semantics/java">
  <rule name="FlagAsUndervalued">
    <parameter identifier="stock">
      <java:class>org.codehaus.drools.example.Stock</java:class>
    </parameter>
    <java:condition>stock.getIndexName().equals("DJIA");</java:condition>
    <java:condition>stock.getPE() > 10 </java:condition>
    <java:consequence>
      removeObject(stock);   ( 译注:应该是retractObject(stock) )
    </java:consequence>
  </rule>
</rule-set>

       现在的规则文件并没有上面的简洁明了。别担心,我们将在下一篇文章讨论语义模块。现在,请注意观察XML文件的结构。其中一个rule-set元素包含了 一个或多个rule元素,rule元素又包含了parameter,condition和consequence元素。Condition和 consequence元素包含的内容和Java很象。注意,在这些元素中,有些事你可以做,有些事你不能做。目前,Drools使用 BeanShell2.0b1作为它的Java解释器。我在这里并不想详细的讨论DRL文件和Java语义模块的语法。我们的目标是解释如何使用 Drools的JSR94 API。
       在Drools项目CVS的drools-jsr94模块中,单元测试代码包含了一个ExampleRuleEngineFacade对象,它基于 Brian Topping的Dentaku项目。这个fa?ade对象通过javax.rules API,创建了供RuleExecutionSet和RuleSession使用的一系列对象。它并没有完全包括了Drools引擎API的所有特性和细 微差别,但可以作为新手使用API的一个简单例子。
      下面的代码片断显示如何使用规则引擎的facade构造一个RuleExecutionSet对象,并通过它获得一个RuleSession对象。
 
import java.io.InputStream;
import javax.rules.*;
import org.drools.jsr94.rules.ExampleRuleEngineFacade;
public class Example {
    private ExampleRuleEngineFacade engine;
    private StatelessRuleSession statelessSession;
    /* place the rule file in the same package as this class */
    private String bindUri = "myRuleFile.drl"
    public Example() {
        /* get your engine facade */
        engine = new ExampleRuleEngineFacade();
        /* get your input stream */
        InputStream inputStream =
                Example.class.getResourceAsStream(bindUri);
        /* build a RuleExecutionSet to the engine */
        engine.addRuleExecutionSet(bindUri, inputStream);
        /* don't forget to close your InputStream! */
        inputStream.close();
        /* get your runtime session */
        this.statelessSession = engine.getStatelessRuleSession(bindUri);
    }
    ...
}

       在以上的例子代码中,你需要处理InputStream的IOException例外,这里为了简单起见省略了。你要做的只是构建InputStream 对象,并把它输入ExampleRuleEngineFacade,用来创建一个RuleExecutionSet对象。然后,你可以得到一个 StatelessRuleSession,并用它来执行所有的规则。使用StatelessRuleSession相对简单。我们可以给上面的类添加一 个方法,用来对一个对象列表执行规则:

public List getUndervalued(List stocks) {
    return statelessSession.executeRules(stocks);
}

       该方法输入一个stock对象列表给规则引擎,然后使用规则评估输入的股票对象,并剔除那些不符合价值低估标准的股票。它是个简单的例子,但足以说明问题。
       在ExampleRuleEngineFacade类中,代码会稍微有些复杂。ExampleRuleEngineFacade类创建了一个 RuleServiceProvider对象,并用它创建RuleAdministrator,RuleExecutionSetProvider和 RuleRuntime对象。RuleExecutionSetProvider负责解释InputStream,并创建一个 RuleExecutionSet对象。RuleRuntime对象用来得到一个session,RuleAdministrator用来管理所有的对 象。在往下是Drools核心API,它的核心是Rete算法实现。我在这里不打算详细讨论,但你可以看看 ExampleRuleEngineFacade的代码。
       现在你已经看到了在商业和科研方面使用规则引擎的一些例子,并对Drools项目有了基本的了解。在下一篇文章里,我将讨论DRL文件的结构和Java语 义模块,让你可以编写自己的DRL文件。还将向你解释如何编写你自己的语义模块,讨论salience和working memory的概念。

资源
· Drools Project
· JSR-94 Specification
 
作者
      N. Alex Rupp is a freelance software architect and developer from Minneapolis, and the current JSR94 Lead for the Drools project.
posted @ 2006-03-11 10:00 Dion 阅读(1722) | 评论 (0)编辑 收藏

一般情况下, 只显式引用:

  • drools-all-2.0.jar
  • antlr-2.7.5.jar
  • xercesImpl-2.6.2.jar

就可以了.当然ClassPath下也要用一些其他的jar.
下载位置: http://dist.codehaus.org/drools/distributions/drools-2.0-bin-withdeps.zip

如果, 在DRL文件中定义了Java Function, 这时候就要显式的引用:

  • janino-2.3.2.jar

这时候, 引擎是需要janino把DRL中的java function描述转换成可执行的二进制代码(?)的.

posted @ 2006-03-11 09:59 Dion 阅读(952) | 评论 (0)编辑 收藏

Drools and Mandarax

两个项目做了两件不同的事情: 一个是Forward Chaining,另一个是 backward chaining. Drools 是forward chaining的,  意味着 它对assert的对象反应, 事件驱动的. Mandarax 是 backward chaining的, 像 prologue一样, 你问它问题, 它试图给你它知道的答案. 举例来说, 在使用Drools的时候, 你可能会先assert 给它今天的日期, 如果它发现有匹配的规则的手,它会用事件的方式通知你"今天是你的生日". 在 backward chaining 的系统, 你可能先问 "今天是我的生日嘛?" 系统会搜索它知道的, 然后告诉你答案.
For an excellent explanation of forward and backward chaining read Charles Forgey's recent articles at http://rulespower.com/ - Forward and Backward Chaining:
Parts 1, 2 and 3.
posted @ 2006-03-11 09:58 Dion 阅读(1162) | 评论 (0)编辑 收藏

Open Source Rule Engines Written In Java

  • Drools The drools engine uses a modified form of the Rete algorithm called the Rete-OO algorithm. Internally it operates using the same concepts and methods as Forgy's original but adds some node types required for seemless integration with an object-oriented language.
  • OFBiz Rule Engine Backward chaining is supported. Original code base from "Building Parsers in Java" by Steven John Metsker.
  • Mandarax Based on backward reasoning. The easy integration of all kinds of data sources. E.g., database records can be easily integrated as sets of facts and reflection is used in order to integrate functionality available in the object model.
  • Algernon Efficient and concise KB traversal and retrieval. Straightforward access to ontology classes and instances. Supports both forward and backward chaining.
  • TyRuBa TyRuBa supports higher order logic programming: variables and compound terms are allowed everywhere in queries and rules, also in the position of a functor- or predicate-name. TyRuBa speeds up execution by making specialized copies of the rule-base for each query in the program. It does so incrementally while executing a logic program and builds an index for fast access to rules and facts in the rule base, tuned to the program that is running. The indexing techniques works also for higher-order logic. TyRuBa does 'tabling' of query results.
  • JTP Java Theorem Prover is based on a very simple and general reasoning architecture. The modular character of the architecture makes it easy to extend the system by adding new reasoning modules (reasoners), or by customizing or rearranging existing ones.
  • JEOPS JEOPS adds forward chaining, first-order production rules to Java through a set of classes designed to provide this language with some kind of declarative programming.
  • InfoSapient Semantics of business rules expressed using fuzzy logic.
  • JShop Simple Hierarchical Ordered Planner (SHOP) written in Java.
  • RDFExpert RDF-driven expert system shell. The RDFExpert software uses Brian McBride's JENA API and parser. A simple expert system shell that uses RDF for all of its input: knowledge base, inference rules and elements of the resolution strategy employed. It supports forward and backward chaining.
  • Jena 2 - Jena is a Java framework for writing Semantic Web applications. Jena2 has a reasoner subsystem which includes a generic rule based inference engine together with configured rule sets for RDFS and for the OWL/Lite subset of OWL Full. These reasoners can be used to construct inference models which show the RDF statements entailed by the data being reasoned over. The subsystem is designed to be extensible so that it should be possible to plug a range of external reasoners into Jena, though worked examples of doing so are left to a future release.
  • JLisa - JLisa is a powerful framework for building business rules accessible to Java and it is compatible with JSR-94. JLisa is more powerful than Clips because it has the expanded benefit of having all the features from common lisp available. These features are essential for multi-paradigm software development
  • Euler - Euler is a backward-chaining reasoner enhanced with Euler path detection and will tell you whether a given set of facts and rules supports a given conclusion. Things are described in N3.
  • JLog - JLog is an implementation of a Prolog interpreter, written in Java. It includes built-in source editor, query panels, online help, animation primitives, and a GUI debugger.
  • Pellet OWL Reasoner - Pellet is an open-source Java based OWL DL reasoner. It can be used in conjunction with either Jena or OWL API libraries. Pellet API provides functionalities to see the species validation, check consistency of ontologies, classify the taxonomy, check entailments and answer a subset of RDQL queries (known as ABox queries in DL terminology). Pellet is an OWL DL reasoner based on the tableaux algorithms developed for expressive Description Logics.
  • Prova - Prova is derived from Mandarax Java-based inference system developed by Jens Dietrich. Prova extends Mandarax by providing a proper language syntax, native syntax integration with Java, and agent messaging and reaction rules. The development of this language was supported by the grant provided within the EU project GeneStream. In the project, the language is used as a rules-based backbone for distributed web applications in biomedical data integration.
posted @ 2006-03-11 09:58 Dion 阅读(1331) | 评论 (0)编辑 收藏

始终会用上的Common BeanUtils

Beanutils用了魔术般的反射技术,实现了很多夸张有用的功能,都是C/C++时代不敢想的。无论谁的项目,始终一天都会用得上它。我算是后知后觉了,第一回看到它的时候居然错过。

1.属性的动态getter、setter

在这框架满天飞的年代,不能事事都保证执行getter,setter函数了,有时候属性是要根据名字动态取得的,就像这样:  
BeanUtils.getProperty(myBean,"code");
而Common BeanUtils的更强功能在于可以直接访问内嵌对象的属性,只要使用点号分隔。
BeanUtils.getProperty(orderBean, "address.city");
相比之下其他类库的BeanUtils通常都很简单,不能访问内嵌的对象,所以有时要用Commons BeanUtils来替换它们。

BeanUtils还支持List和Map类型的属性,如下面的语法即可取得Order的顾客列表中第一个顾客的名字
BeanUtils.getProperty(orderBean, "customers[1].name");
其中BeanUtils会使用ConvertUtils类把字符串转为Bean属性的真正类型,方便从HttpServletRequest等对象中提取bean,或者把bean输出到页面。
而PropertyUtils就会原色的保留Bean原来的类型。

2.BeanCompartor 动态排序

还是通过反射,动态设定Bean按照哪个属性来排序,而不再需要在实现bean的Compare接口进行复杂的条件判断。
List peoples = ...; // Person对象的列表
Collections.sort(peoples, new BeanComparator("age"));

如果要支持多个属性的复合排序,如"Order By lastName,firstName"

ArrayList sortFields = new ArrayList();
sortFields.add(new BeanComparator("lastName"));
sortFields.add(new BeanComparator("firstName"));
ComparatorChain multiSort = new ComparatorChain(sortFields);
Collections.sort(rows,multiSort);

其中ComparatorChain属于jakata commons-collections包。
如果age属性不是普通类型,构造函数需要再传入一个comparator对象为age变量排序。
另外, BeanCompartor本身的ComparebleComparator, 遇到属性为null就会抛出异常, 也不能设定升序还是降序。这个时候又要借助commons-collections包的ComparatorUtils.

   Comparator mycmp = ComparableComparator.getInstance();
   mycmp = ComparatorUtils.nullLowComparator(mycmp);  //允许null
   mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序
   Comparator cmp = new BeanComparator(sortColumn, mycmp);
3.Converter 把Request或ResultSet中的字符串绑定到对象的属性

   经常要从request,resultSet等对象取出值来赋入bean中,如果不用MVC框架的绑定功能的话,下面的代码谁都写腻了。

   String a = request.getParameter("a");
bean.setA(a);
String b = ....
bean.setB(b);
......

不妨写一个Binder自动绑定所有属性:

    MyBean bean = ...;
HashMap map = new HashMap();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements())
{
String name = (String) names.nextElement();
map.put(name, request.getParameterValues(name));
}
BeanUtils.populate(bean, map);

    其中BeanUtils的populate方法或者getProperty,setProperty方法其实都会调用convert进行转换。
     但Converter只支持一些基本的类型,甚至连java.util.Date类型也不支持。而且它比较笨的一个地方是当遇到不认识的类型时,居然会抛 出异常来。 对于Date类型,我参考它的sqldate类型实现了一个Converter,而且添加了一个设置日期格式的函数。
要把这个Converter注册,需要如下语句:

    ConvertUtilsBean convertUtils = new ConvertUtilsBean();
   DateConverter dateConverter = new DateConverter();
   convertUtils.register(dateConverter,Date.class);



//因为要注册converter,所以不能再使用BeanUtils的静态方法了,必须创建BeanUtilsBean实例
BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());
beanUtils.setProperty(bean, name, value);
4 其他功能
4.1 ConstructorUtils,动态创建对象
     public static Object invokeConstructor(Class klass, Object arg)
4.2 MethodUtils,动态调用方法
    MethodUtils.invokeMethod(bean, methodName, parameter);

4.3 PropertyUtils,当属性为Collection,Map时的动态读取:
Collection: 提供index
   BeanUtils.getIndexedProperty(orderBean,"items",1);
或者
  BeanUtils.getIndexedProperty(orderBean,"items[1]");
Map: 提供Key Value
  BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111 
或者
  BeanUtils.getMappedProperty(orderBean, "items(111)") 

4.4 PropertyUtils,直接获取属性的Class类型
     public static Class getPropertyType(Object bean, String name)
4.5 动态Bean 用DynaBean减除不必要的VO和FormBean 
posted @ 2006-01-15 20:20 Dion 阅读(5081) | 评论 (2)编辑 收藏

     摘要: Migrate apps from Internet Explorer to MozillaHow to make Internet Explorer-specific Web applications work in Mozilla-based browsersDocument options Print this page'); //--> Print this page E-ma...  阅读全文
posted @ 2006-01-04 19:18 Dion 阅读(1481) | 评论 (1)编辑 收藏

以下以 IE 代替 Internet Explorer,以 MF 代替 Mozzila Firefox

1. document.form.item 问题
    (1)现有问题:
        现有代码中存在许多 document.formName.item("itemName") 这样的语句,不能在 MF 下运行
    (2)解决方法:
        改用 document.formName.elements["elementName"]
    (3)其它
        参见 2

2. 集合类对象问题
    (1)现有问题:
        现有代码中许多集合类对象取用时使用 (),IE 能接受,MF 不能。
    (2)解决方法:
        改用 [] 作为下标运算。如:document.forms("formName") 改为 document.forms["formName"]。
        又如:document.getElementsByName("inputName")(1) 改为 document.getElementsByName("inputName")[1]
    (3)其它

3. window.event
    (1)现有问题:
        使用 window.event 无法在 MF 上运行
    (2)解决方法:
        MF 的 event 只能在事件发生的现场使用,此问题暂无法解决。可以这样变通:
        原代码(可在IE中运行):
            <input type="button" name="someButton" value="提交" onclick="javascript:gotoSubmit()"/>
            ...
            <script language="javascript">
                function gotoSubmit() {
                    ...
                    alert(window.event);    // use window.event
                    ...
                }
            </script>

        新代码(可在IE和MF中运行):
            <input type="button" name="someButton" value="提交" onclick="javascript:gotoSubmit(event)"/>
            ...
            <script language="javascript">
                function gotoSubmit(evt) {
                    evt = evt ? evt : (window.event ? window.event : null);
                    ...
                    alert(evt);             // use evt
                    ...
                }
            </script>
        此外,如果新代码中第一行不改,与老代码一样的话(即 gotoSubmit 调用没有给参数),则仍然只能在IE中运行,但不会出错。所以,这种方案 tpl 部分仍与老代码兼容。

4. HTML 对象的 id 作为对象名的问题
    (1)现有问题
        在 IE 中,HTML 对象的 ID 可以作为 document 的下属对象变量名直接使用。在 MF 中不能。
    (2)解决方法
        用 getElementById("idName") 代替 idName 作为对象变量使用。

5. 用idName字符串取得对象的问题
    (1)现有问题
        在IE中,利用 eval(idName) 可以取得 id 为 idName 的 HTML 对象,在MF 中不能。
    (2)解决方法
        用 getElementById(idName) 代替 eval(idName)。

6. 变量名与某 HTML 对象 id 相同的问题
    (1)现有问题
        在 MF 中,因为对象 id 不作为 HTML 对象的名称,所以可以使用与 HTML 对象 id 相同的变量名,IE 中不能。
    (2)解决方法
        在声明变量时,一律加上 var ,以避免歧义,这样在 IE 中亦可正常运行。
        此外,最好不要取与 HTML 对象 id 相同的变量名,以减少错误。
    (3)其它
        参见 问题4

7. event.x 与 event.y 问题
    (1)现有问题
        在IE 中,event 对象有 x, y 属性,MF中没有。
    (2)解决方法
        在MF中,与event.x 等效的是 event.pageX。但event.pageX IE中没有。
        故采用 event.clientX 代替 event.x。在IE 中也有这个变量。
        event.clientX 与 event.pageX 有微妙的差别(当整个页面有滚动条的时候),不过大多数时候是等效的。

        如果要完全一样,可以稍麻烦些:
        mX = event.x ? event.x : event.pageX;
        然后用 mX 代替 event.x
    (3)其它
        event.layerX 在 IE 与 MF 中都有,具体意义有无差别尚未试验。


8. 关于frame
   (1)现有问题
         在 IE中 可以用window.testFrame取得该frame,mf中不行
   (2)解决方法
         在frame的使用方面mf和ie的最主要的区别是:
如果在frame标签中书写了以下属性:
<frame src="xx.htm" id="frameId" name="frameName" />
那么ie可以通过id或者name访问这个frame对应的window对象
而mf只可以通过name来访问这个frame对应的window对象
例如如果上述frame标签写在最上层的window里面的htm里面,那么可以这样访问
ie: window.top.frameId或者window.top.frameName来访问这个window对象
mf: 只能这样window.top.frameName来访问这个window对象

另外,在mf和ie中都可以使用window.top.document.getElementById("frameId")来访问frame标签
并且可以通过window.top.document.getElementById("testFrame").src = 'xx.htm'来切换frame的内容
也都可以通过window.top.frameName.location = 'xx.htm'来切换frame的内容
关于frame和window的描述可以参见bbs的‘window与frame’文章
以及/test/js/test_frame/目录下面的测试
----adun 2004.12.09修改

9. 在mf中,自己定义的属性必须getAttribute()取得
10.在mf中没有  parentElement parement.children  而用
               parentNode parentNode.childNodes
   childNodes的下标的含义在IE和MF中不同,MF使用DOM规范,childNodes中会插入空白文本节点。
  一般可以通过node.getElementsByTagName()来回避这个问题。
   当html中节点缺失时,IE和MF对parentNode的解释不同,例如
   <form>
   <table>
        <input/>
   </table>
   </form>
   MF中input.parentNode的值为form, 而IE中input.parentNode的值为空节点

  MF中节点没有removeNode方法,必须使用如下方法 node.parentNode.removeChild(node)

11.const 问题
  (1)现有问题:
     在 IE 中不能使用 const 关键字。如 const constVar = 32; 在IE中这是语法错误。
  (2)解决方法:
     不使用 const ,以 var 代替。

12. body 对象
   MF的body在body标签没有被浏览器完全读入之前就存在,而IE则必须在body完全被读入之后才存在

13. url encoding
在js中如果书写url就直接写&不要写&amp;例如var url = 'xx.jsp?objectName=xx&amp;objectEvent=xxx';
frm.action = url那么很有可能url不会被正常显示以至于参数没有正确的传到服务器
一般会服务器报错参数没有找到
当然如果是在tpl中例外,因为tpl中符合xml规范,要求&书写为&amp;
一般MF无法识别js中的&amp;


14. nodeName 和 tagName 问题
  (1)现有问题:
     在MF中,所有节点均有 nodeName 值,但 textNode 没有 tagName 值。在 IE 中,nodeName 的使用好象
     有问题(具体情况没有测试,但我的IE已经死了好几次)。
  (2)解决方法:
     使用 tagName,但应检测其是否为空。

15. 元素属性
   IE下 input.type属性为只读,但是MF下可以修改


16. document.getElementsByName() 和 document.all[name] 的问题
  (1)现有问题:
     在 IE 中,getElementsByName()、document.all[name] 均不能用来取得 div 元素(是否还有其它不能取的元素还不知道)。
posted @ 2005-12-31 17:55 Dion 阅读(926) | 评论 (1)编辑 收藏