Jess、XML与企业级开发:规则处理与EJB集成
1. Jess规则的XML表示
在规则处理中,XML是一种优秀的规则存储方式。通过定义DTD(文档类型定义),可以规范地用XML表示Jess规则。以下是相关元素的定义:
-
<group>
元素:可以包含一个或多个
<group>
或
<pattern>
元素,且必须有
name
属性。
<!ELEMENT group (group | pattern)+>
<!ATTLIST group name CDATA #REQUIRED>
-
<pattern>元素:可以包含零个或多个<slot>元素,有name和binding属性。
<!ELEMENT pattern (slot*)>
<!ATTLIST pattern name CDATA #REQUIRED binding CDATA "">
-
<slot>元素:有name属性,可包含零个或多个<variable>、<constant>或<function - call>元素。
<!ELEMENT slot (variable | constant | function - call)*>
<!ATTLIST slot name CDATA #REQUIRED>
-
<variable>元素:仅包含name属性。
<!ELEMENT variable EMPTY>
<!ATTLIST variable name CDATA #REQUIRED>
-
<function - call>元素:包含一个<head>元素,后面可跟零个或多个<constant>、<variable>或其他<function - call>元素。
<!ELEMENT function - call (head,(constant|variable|function - call)*)>
<!ELEMENT head (#PCDATA)>
<!ELEMENT constant (#PCDATA)>
这些定义对Jess构造的使用有一定限制,比如必须使用命名槽,不能使用
|
(或)连接符。但这并不影响可表达的模式,例如可以将
(ball (color red | blue))
改写为
(ball (color ?color&:(or (eq ?color red) (eq ?color blue))))
。
示例规则
以规则
(defrule AnimalRule2 (declare (salience 10)) ?animal <- (animal (has - hair TRUE)) => (modify ?animal (type mammal)))
为例,其XML表示如下:
<?xml version="1.0"?>
<!DOCTYPE rulebase SYSTEM "jess.dtd">
<rulebase>
<rule name="AnimalRule2" priority="10">
<lhs>
<pattern name="animal" binding="animal">
<slot name="has - hair">
<constant>TRUE</constant>
</slot>
</pattern>
</lhs>
<rhs>
<function - call>
<head>modify</head>
<variable>animal</variable>
<constant>(type mammal)</constant>
</function - call>
</rhs>
</rule>
</rulebase>
2. XML规则转换为Jess规则
可以使用约100行的XSLT脚本将XML规则格式转换为Jess规则。XSLT程序是声明式的,类似于Jess中的规则。以下是一个处理
<variable>
节点的XSLT转换规则示例:
<xsl:template match="variable">
<xsl:text> ?</xsl:text>
<xsl:value - of select="@name"/>
</xsl:template>
这个规则将XML片段
<variable name="color"/>
转换为Jess片段
“ ?color”
。
完整的XSLT脚本如下:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" indent="no"/>
<xsl:strip - space elements="*"/>
<!-- Top - level rule template -->
<xsl:template match="rule">
<xsl:text>(defrule </xsl:text>
<xsl:value - of select="@name"/>
<xsl:text>
</xsl:text>
<xsl:if test="@priority != ''">
<xsl:text> (declare (salience </xsl:text>
<xsl:value - of select="./@priority"/>
<xsl:text>))
</xsl:text>
</xsl:if>
<xsl:apply - templates select="./lhs"/>
<xsl:text> =></xsl:text>
<xsl:apply - templates select="./rhs"/>
<xsl:text>)
</xsl:text>
</xsl:template>
<!-- Rule left hand sides -->
<xsl:template match="lhs">
<xsl:for - each select="./group | ./pattern">
<xsl:text> </xsl:text>
<xsl:apply - templates select="."/>
<xsl:text>
</xsl:text>
</xsl:for - each>
</xsl:template>
<xsl:template match="group">
<xsl:text>(</xsl:text>
<xsl:value - of select="./@name"/>
<xsl:text> </xsl:text>
<xsl:apply - templates/>
<xsl:text>)</xsl:text>
</xsl:template>
<xsl:template match="pattern">
<xsl:if test="@binding != ''">
<xsl:text>?</xsl:text>
<xsl:value - of select="@binding"/>
<xsl:text> <- </xsl:text>
</xsl:if>
<xsl:text>(</xsl:text>
<xsl:value - of select="./@name"/>
<xsl:apply - templates select="./slot"/>
<xsl:text>)</xsl:text>
</xsl:template>
<xsl:template match="slot">
<xsl:text> (</xsl:text>
<xsl:value - of select="./@name"/>
<xsl:for - each select="./*">
<xsl:if test="position() != 1">
<xsl:text>&</xsl:text>
</xsl:if>
<xsl:apply - templates select="."/>
</xsl:for - each>
<xsl:text>)</xsl:text>
</xsl:template>
<xsl:template match="slot/function - call">
<xsl:text>:</xsl:text>
<xsl:call - template name="funcall"/>
</xsl:template>
<!-- Rule right hand sides -->
<xsl:template match="rhs/function - call">
<xsl:text>
 </xsl:text>
<xsl:call - template name="funcall"/>
<xsl:text></xsl:text>
</xsl:template>
<!-- Function calls -->
<xsl:template match="function - call">
<xsl:call - template name="funcall"/>
</xsl:template>
<xsl:template name="funcall">
<xsl:text>(</xsl:text>
<xsl:apply - templates select="./*"/>
<xsl:text>)</xsl:text>
</xsl:template>
<xsl:template match="function - call/function - call">
<xsl:text> </xsl:text>
<xsl:call - template name="funcall"/>
</xsl:template>
<!-- Miscellaneous -->
<xsl:template match="variable">
<xsl:text> ?</xsl:text>
<xsl:value - of select="@name"/>
</xsl:template>
<xsl:template match="constant">
<xsl:text> </xsl:text>
<xsl:value - of select="."/>
</xsl:template>
</xsl:stylesheet>
3. 规则编辑器
如果规则用XML文档表示,可以使用XML编辑器创建规则。一些XML编辑器是图形化的,可通过拖动元素到树结构中编辑文档。若XML格式由DTD严格描述,许多编辑器可限制创建的文档为格式良好的规则文档。但对于非技术用户,使用XML编辑器通常过于复杂,一个可自定义显示以进行规则编辑的编辑器会更易用。
Jess当前版本没有自带规则编辑器,但可以用Java编写自定义编辑器。使用XML规则格式可简化此过程,因为标准XML库可轻松处理输入输出文件的解析和写入。以下是加载和写入XML文件的示例代码:
import java.io.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import javax.xml.parsers.*;
public class RuleLoader {
public Document readInRules(String filename) throws Exception {
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
FileReader reader = new FileReader(filename);
try {
return docBuilder.parse(new InputSource(reader));
} finally {
reader.close();
}
}
}
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
public class RuleWriter {
public void writeOutRules(Document rules, String filename) throws Exception {
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
DOMSource source = new DOMSource(rules);
FileWriter writer = new FileWriter(filename);
StreamResult result = new StreamResult(writer);
try {
transformer.transform(source, result);
} finally {
writer.close();
}
}
}
在读取和写入之间,可以编写代码实现规则编辑,例如使用Swing GUI或基于Servlet和JSP的Web界面。创建一个完全通用的规则编辑器可能很困难,但创建一个允许用户在特定约束下添加规则的有限编辑器会简单得多。
4. EJB概念概述
Enterprise JavaBeans(EJB)是用于服务器端Java编程的组件架构。EJB是相对较小、相对独立的代码组件,嵌入在容器中,容器为EJB提供诸如生命周期管理、网络连接、数据库访问、持久化、复制、事务处理等基本服务。
EJB的类型
- 实体Bean :代表具体对象或概念,如人员或产品。通常每个实体Bean对应底层数据库中的一行,EJB容器可自动处理其持久化,无需编写数据库代码。
- 会话Bean :代表操作,包含协调应用程序组件的业务逻辑。有状态会话Bean有短期持久化,如购物车;无状态会话Bean仅封装代码,不包含客户端特定数据。
- 消息驱动Bean :是EJB 2.0规范中的新组件,用于将Java消息服务(JMS)集成到EJB容器中,JMS是一种异步、基于事件的通信机制。
EJB的限制
为提供服务,EJB容器需要严格控制内部状态,因此EJB有一些使用限制:
- 不允许使用线程或同步。
- 不允许使用
java.io
包访问文件。
- 不允许显示GUI。
- 不允许接收传入的套接字连接。
- 不允许设置全局对象,如套接字工厂和
System.out
。
- 不允许加载本地库。
- 不允许使用静态变量。
部分限制较易理解,如从服务器端组件显示GUI;但有些限制会带来麻烦,如不能使用同步方法,许多非平凡代码(包括Jess)至少会使用一些同步。不过,如果了解这些限制,部分限制可安全忽略,例如若应用程序不使用复制服务,可忽略关于静态数据的限制。
是否需要使用EJB
编写符合EJB规范的程序可免费获得可扩展性、可用性、对象持久化、事务管理等工业级特性和服务。但使用EJB也有代价:
-
限制问题
:如果应用程序是传统的数据库驱动应用且无需大量计算,限制影响不大;但如果需要使用本地库代码、与无EJB接口的遗留系统交互或集成大量现有Java代码,绕过这些限制会很困难。
-
编程复杂性
:J2EE参考实现有近十几个命令行工具用于管理EJB开发,商业J2EE环境通常通过图形界面提供相同功能。市场上有昂贵的Java开发环境企业版,包含用于自动化EJB开发过程的图形工具。
-
性能成本
:所有基础设施会带来一定的性能开销,可扩展性并不意味着高性能。
是否使用EJB取决于是否需要EJB提供的高级服务,如果只是跟风使用,可能会遇到问题。
从EJB访问外部资源
如果决定使用EJB,通常需要将其连接到一些无法直接集成到EJB环境的外部资源。Jess使用多线程和同步方法,技术上不能在EJB容器内使用。有以下几种集成外部资源的方法:
-
忽略限制直接使用
:这种方法虽不推荐,但广泛使用且看似成功。不过,在参考实现或单处理器应用服务器上运行可能没问题,若部署到服务器群集,应用服务器的持久化和复制服务可能会失败。
-
在单独应用中运行资源
:让外部资源在单独进程中运行,EJB通过进程间通信与其通信。
-
使用J2EE连接器架构(JCA)
:这是连接外部资源到J2EE服务器的标准Java API,但实现JCA连接器较复杂。使用已连接的资源通常很简单,通常只需从Java命名和目录接口(JNDI)获取资源引用。商业应用服务器中集成的规则引擎通常也以这种方式提供。
5. 基于RMI的规则服务器
一种从EJB环境使用外部资源(如规则引擎)的方法是将资源封装在远程方法调用(RMI)服务器中。RMI是J2EE API,用于在不同Java应用程序(同一机器或网络上)之间通信,大多数EJB环境使用RMI进行客户端/服务器通信。通过让外部资源在单独进程中运行,可避免EJB编程限制带来的问题。
RMI通过特殊的存根(stub)和骨架(skeleton)类创建一种错觉,使一个Java应用程序中的对象看起来存在于另一个应用程序中,第二个应用程序可正常调用远程对象的方法。存根类的实例伪装成本地对象,其方法将参数打包成网络请求并转发到远程对象;骨架类接收请求,解包参数并在真实对象上调用方法,返回值再打包返回给存根方法。
编写RMI服务器基本有四个步骤:
1. 定义远程接口。
2. 实现远程接口。
3. 编写服务器主线程。
4. 运行
rmic
工具创建存根和骨架类。
对于Jess服务器,需要两种远程对象。远程接口定义了对象可远程调用的方法。后续可开发一个基于RMI的规则服务器,用于EJB或其他场景。
综上所述,在企业级开发中,Jess规则的XML表示、转换以及与EJB的集成是重要的技术点。XML规则的使用方便了规则的存储和转换,规则编辑器的开发为非技术用户提供了创建和修改规则的途径,而EJB和RMI技术则为规则引擎在企业级应用中的使用提供了强大的支持。在实际应用中,需要根据具体需求权衡是否使用EJB以及选择合适的集成方式。
Jess、XML与企业级开发:规则处理与EJB集成
6. 开发基于RMI的Jess规则服务器
6.1 定义远程接口
远程接口定义了可以远程调用的方法。以下是一个简单的Jess规则服务器的远程接口示例:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface JessRuleServer extends Remote {
// 执行规则的方法
String executeRules(String ruleXML) throws RemoteException;
}
在这个接口中,定义了一个
executeRules
方法,该方法接收一个包含规则的XML字符串,并返回执行结果的字符串。
6.2 实现远程接口
接下来需要实现这个远程接口:
import jess.JessException;
import jess.Rete;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JessRuleServerImpl implements JessRuleServer {
private final Rete engine;
public JessRuleServerImpl() {
engine = new Rete();
try {
engine.reset();
} catch (JessException ex) {
Logger.getLogger(JessRuleServerImpl.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public String executeRules(String ruleXML) throws RemoteException {
try {
// 这里需要添加将XML规则转换为Jess规则并执行的代码
// 可以使用之前提到的XSLT脚本进行转换
// 示例中简单返回一个消息
return "Rules executed successfully";
} catch (Exception ex) {
Logger.getLogger(JessRuleServerImpl.class.getName()).log(Level.SEVERE, null, ex);
return "Error executing rules: " + ex.getMessage();
}
}
}
在这个实现中,创建了一个Jess的
Rete
引擎,并实现了
executeRules
方法。在实际应用中,需要添加将XML规则转换为Jess规则并执行的代码。
6.3 编写服务器主线程
以下是服务器主线程的代码:
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
public class JessRuleServerMain {
public static void main(String[] args) {
try {
// 创建并启动RMI注册表
LocateRegistry.createRegistry(1099);
// 创建规则服务器实例
JessRuleServer server = new JessRuleServerImpl();
// 将规则服务器绑定到RMI注册表
Naming.rebind("JessRuleServer", server);
System.out.println("Jess Rule Server is running...");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
在这个主线程中,创建并启动了RMI注册表,创建了规则服务器实例,并将其绑定到RMI注册表中。
6.4 运行
rmic
工具创建存根和骨架类
在命令行中,进入包含
JessRuleServerImpl
类的目录,运行以下命令:
rmic JessRuleServerImpl
这将生成
JessRuleServerImpl_Stub
和
JessRuleServerImpl_Skel
类,分别是存根和骨架类。
7. 客户端调用RMI规则服务器
以下是一个简单的客户端调用示例:
import java.rmi.Naming;
public class JessRuleClient {
public static void main(String[] args) {
try {
// 查找RMI服务器
JessRuleServer server = (JessRuleServer) Naming.lookup("rmi://localhost:1099/JessRuleServer");
// 准备规则XML
String ruleXML = "<rulebase>...</rulebase>"; // 这里需要替换为实际的规则XML
// 调用服务器的方法
String result = server.executeRules(ruleXML);
System.out.println("Result: " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
在这个客户端代码中,通过
Naming.lookup
方法查找RMI服务器,并调用其
executeRules
方法。
8. 总结与展望
在企业级开发中,将Jess规则与XML和EJB集成是一种强大的组合。XML作为规则的存储格式,具有易解析、易转换和易搜索的优点,方便了规则的管理和维护。通过XSLT脚本,可以轻松地将XML规则转换为Jess规则,实现规则的自动化处理。
EJB虽然提供了强大的企业级服务,但也存在一些限制。在使用EJB时,需要根据应用的具体需求权衡是否使用以及如何处理与外部资源(如Jess规则引擎)的集成。RMI作为一种有效的跨进程通信方式,为解决EJB与外部资源集成问题提供了一种可行的方案。
未来,可以进一步探索以下方面:
-
规则编辑器的优化
:开发更友好、功能更强大的规则编辑器,支持更多的规则编辑操作和可视化展示,降低非技术用户的使用门槛。
-
性能优化
:对规则处理和EJB应用进行性能优化,例如优化XSLT转换脚本、减少RMI通信开销等。
-
与其他技术的集成
:将Jess规则与更多的企业级技术集成,如微服务架构、大数据处理等,拓展规则引擎的应用场景。
通过不断地探索和实践,Jess、XML和EJB的集成将在企业级开发中发挥更大的作用,为企业提供更高效、更智能的规则处理解决方案。
9. 流程图总结
以下是整个规则处理和EJB集成的流程图:
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
A(规则创建):::process --> B(XML规则存储):::process
B --> C(XSLT转换):::process
C --> D(Jess规则执行):::process
E(EJB应用):::process --> F(与外部资源集成):::process
F --> G(RMI通信):::process
G --> D
D --> H(结果返回):::process
H --> E
这个流程图展示了从规则创建到执行,以及EJB与外部资源(Jess规则引擎)集成的整个过程。规则首先以XML格式存储,然后通过XSLT转换为Jess规则进行执行。EJB应用通过RMI与Jess规则服务器通信,获取规则执行结果。
10. 表格总结
| 技术点 | 描述 |
|---|---|
| XML规则表示 | 通过DTD定义XML元素,规范Jess规则的XML表示,便于存储和管理。 |
| XSLT转换 | 使用约100行的XSLT脚本将XML规则转换为Jess规则,实现规则的自动化处理。 |
| 规则编辑器 | 可以使用XML编辑器或自定义Java编辑器创建和修改规则,简化规则编辑过程。 |
| EJB | 企业级Java组件架构,提供多种服务,但有使用限制,需要根据需求权衡是否使用。 |
| RMI | 用于在不同Java应用程序之间通信,解决EJB与外部资源集成问题。 |
通过这个表格,可以清晰地看到各个技术点的主要特点和作用。
Jess、XML与EJB在企业级开发的集成应用
超级会员免费看
39

被折叠的 条评论
为什么被折叠?



