JFox(J2EE应用服务器开源项目)

JFox2.0 中文文档

作者:
orbat <young_yy AT hotmail.com , JFox Team>
Sep 2004


本文阐述安装和使用JFox的方法,同时概观地说明EJB应用的开发、编译、调试、部署、执行的过程以及可能面临的一些问题。 写作的参考资料来源于JFox的设计,工作,使用等文档,有任何指正与建议的,我们都很欢迎。


1.概览
    JFox(http://sourceforge.net/projects/jfox) 是基于J2EE的应用服务器,遵循ejb2.0规范,为了简化EJB的开发,提高开发和部署的效率,在表达形式上也做了一些改进,我们的目标是在规范和开发效率之间找到一个平衡,既尽可能遵循规范,又尽可能改变ejb开发缓慢、布署麻烦的现状,切实提供一个快速的J2EE中间件平台。

2.产品特性
    1.符合 ejb2.0 SessionBean规范
      暂不支持EntityBean,推荐采用SessionBean + DAO/OR 来进行数据持久化
    2.采用增强的IOC 内核,并结合JMX的优势,支持Web组建管理
    3.完全支持JTA 1.01b规范,支持两阶段提交(2pc)
    4.支持多种数据源,MySQL、SQLServer、Oracle、DB2
    5.Remote和Local调用自动切换,同一个应用服务器上的ejb之间的调用自动使用Local调用,无需实现Local接口
    6.采用动态代理调用框架,无需ejb预编译
    7.自动发布,拷贝即可发布
      有新的EJB要发布的时候,只要将该ejb的jar包拷贝到%JFOX_HOME%/deploy目录下即可,自动发布器会即时将该ejb布署到应用服务器中
    8.使用Meta作为发布描述文件
      采用Meta方式进行智能化布署,使得布署不再需要繁琐的步骤和不同工种之间的协作,布署的描述由程序员通过编程完成。
    9.协议后决的调用方式
      协议后决的调用方式使得在调用过程可以动态改变调用协议,通过jndi.lookup得到的Home存根是一个与协议无关的存根,当通过该Home存根进行调用的时候,才会选择一种合适的协议,当前对于同JVM之间的调用自动选择为Local调用,对远程调用,则自动选择JRMP协议,也可以通过Home.setProtocol来强行制定同JVM采用JRMP协议进行调用
    10.采用优化的调用链模型,提高服务端执行速度
    11.通过XML-RPC支持Web Service
    12.集成Jetty Web Container
    13.支持EAR部署

3.系统安装
    1.安装JDK,建议1.4以上版本,并设置%JAVA_HOME%环境变量;
    2.从http://sourceforge.net/projects/JFox下载JFox最新版本,解压至某个目录,假定为%JFOX_HOME%
    3.在%JFOX_HOME%/bin下,运行startup.bat启动JFox
    4.连接http://localhost:8080,可以访问JFox的Web界面,在这里可以执行JFox提供的例子
    5.连接 http://localhost:8082,可以看到正在运行的系统组件,并可以进行管理
    6. 连接 http://localhost:8080/jpetstore , 可以运行PetStore的演示

4.应用开发
4.1第一个EJB
    假定我们要开发一个叫做Hello的Stateless Session Bean,步骤如下,具体代码参考%JFOX_HOME%/TestEJB
    1.定义Remote接口,TestStateless.java
    2.定义Home接口,TestStatelessHome.java
    3.实现Hello EJB,HelloBean.java
    4.制作ejb-jar.xml 文件
    5.可选择实现一个Meta类,名称必须为TestStatelessMeta.java
    6.使用jar打包,拷贝至%JFOX_HOME%deploy下,发布完成
    在这些步骤中,前4步都和经典的ejb开发相同,第五步,是在标准允许的情况下,对EJB部署的扩展,但是与一般的采用一个特殊的xml文件(如:jboss采用jboss-ejb-jar.xml)来描述不同,JFox采用一个Meta类来描述JFox特定的部署信息,这样而简化EJB的开发,Meta类可以通过继承org.jfox.ejb.meta.EJBMetaSupport来实现,如果没有提供Meta类,JFox采用默认的特定部署信息,比如:JNDI Name为 ejb/$EJB_NAME$,远程调用协议为JRMP,本地调用采用LOCAL协议。
在这个例子中,我们先忽略Meta类,采用默认的配置,Meta类的编写在后面的章节中详细描述。
    按照EJB的规范,编写好ejb-jar.xml,下面的部分从%FOX_HOME%/TestEJB/META-INF/ejb-jar.xml文件中截取。
Code:
    
<session>
<display-name>Test Stateless SessionBean</display-name>
<ejb-name>TestStatelessEJB</ejb-name>
<home>org.jfox.examples.ejb.stateless.TestStatelessHome</home>
<remote>org.jfox.examples.ejb.stateless.TestStateless</remote>
<ejb-class>org.jfox.examples.ejb.stateless.TestStatelessBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>


    完成之后,编译,并打成一个jar包,并将 ejb-jar.xml放置到jar包中的META-INF 下,这样ejb组件就完成了,将包拷贝到%JFOX_HOME%deploy下即会自动发布,JNDI Name为ejb/TestStatelessEJB,TestStatelessEJB即是ejb-jar.xml中指定的该ejb的名称,这个名称必须是唯一的。

    TestStatelessEJB 的客户端的详细代码可以看%JFOX_HOME%/TestEJB/src/org/jfox/examples/ejb/stateless/TestMain.java,下面是通过JNDI获得生成Hello对象的代码,注意的还是JNDI的名称 ejb/
Code:

TestStatelessEJB Context ctx = new InitialContext();
Object obj = ctx.lookup("/ejb/ TestStatelessEJB ");
home = (HelloHome)javax.rmi.PortableRemoteObject.narrow(obj,HelloHome.class);
hello = home.create();


4.2 EJBMeta的定制
    JFox采用EJBMeta类来描述JFox特有的部署信息,而不是常见的一个扩展的xml来描述,这个类是可选的,如果采用默认的部署描述,可以不提供该类,如果要提供Meta类,该类的名称必须为$EJB_CLASS_NAME$Meta。EJBMeta 要完成最重要的3个描述信息是:远程调用协议、本地调用协议、EJB的JNDI名称。默认情况(没有提供)下,远程调用协议为JRMP,本地调用协议为LOCAL,JNDI Name为ejb/$EJB_NAME$,$EJB_NAME$是在ejb-jar.xml中指定的该ejb的名称。如果你希望远程调用采用其他的协议,比如:JRMP_SSL,SOAP等,或者本地调用不希望采用LOCAL协议,而采用JRMP等远程协议,或者希望采用指定的JNDI名称,则需要提供Meta类。
该类的内容可以参考:
%JFOX_HOME%/TestEJB/src/org/jfox/examples/ejb/stateless/TestStatlessMeta.java,下面是节选的内容:
Code:
public class TestStatelessMeta extends EJBMetaSupport {


public TestStatelessMeta(EJBDescriptor ejbDescriptor) {
super(ejbDescriptor);
}

public Protocol getRemoteProtocol() {
return Protocol.JRMP_SSL;
}

public Protocol getLocalProtocol() {
return super.getLocalProtocol();
}

public String getJndiName() {
return super.getJndiName();
}

}

    它的内容很简单,可以直接从 EJBMetaSupport 继承,然后覆盖必要的方法。
    要注意的是,getRemoteProtocol() 方法不能返回 Protocol.LOCAL协议,理由是很明显的,LOCAL协议只能用在同一虚拟机中对EJB的调用。

4.3事务处理
    为了保证JFox的服务端执行效率和业务安全,JFox实现了一个性能优异的事务处理器,通过和jboss进行对比测试,JFox tm 提供了更好的性能,详情参见:%JFOX_HOME%/docs/JFoxtm-test.doc
    我们推荐你使用容器管理的事务,JFox支持EJB规范定义的所有事务属性。

4.4数据源
JFox 数据源支持所有提供XA规范驱动程序的数据库,现在绝大多数数据库都已提供XA驱动,包括Microsoft SQL Server,SQL Server的jdbc驱动可以在Microsoft的网站上下载到。
要添加一个数据源,可以在%JFOX_HOME%/modules/jdbc/META-INF/module.xml中增加一个component,下面的内容定义了一个不支持XA事务以及一个支持XA事务的数据源
Code:
<?xml version="1.0" encoding="gb2312"  ?>

<module>
<description>JDBC Module</description>
<priority>1</priority>

<component>
<description>Pool DataSource Service(No Transaction)</description>

<implementation>org.jfox.jdbc.datasource.PoolDataSourceService</implementation>
<factor>default</factor>

<singleton>true</singleton>
<type-singleton>false</type-singleton>
<constructor>
<param> <!-- datasource name-->
<type>java.lang.String</type>
<value>TestMysqlDataSource</value>
</param>
<param> <!-- driver class name -->
<type>java.lang.String</type>
<value>com.mysql.jdbc.Driver</value>
</param>
<param> <!-- url -->
<type>java.lang.String</type>
<value>jdbc:mysql://localhost/test</value>
</param>
<param> <!-- user -->
<type>java.lang.String</type>
<value>root</value>
</param>
<param> <!-- password -->
<type>java.lang.String</type>
<value></value>
</param>
</constructor>
</component>

<component>
<description>Pool XADataSource Service(Transaction)</description>

<implementation>org.jfox.jdbc.xa.TxDataSourceService</implementation>
<factor>default</factor>

<singleton>true</singleton>
<type-singleton>false</type-singleton>
<constructor>
<param> <!-- datasource name-->
<type>java.lang.String</type>
<value>TestXAMysqlDataSource</value>
</param>
<param> <!-- url -->
<type>java.lang.String</type>
<value>jdbc:mysql://localhost/test</value>
</param>
<param> <!-- user -->
<type>java.lang.String</type>
<value>root</value>
</param>
<param> <!-- password -->
<type>java.lang.String</type>
<value></value>
</param>
</constructor>
</component>

</module>

    这将创立一个名为TestMysqlDataSource的Mysql数据源和一个TestXAMysqlDataSource的XA Mysql数据源,数据源的名称不仅用来唯一的标志一个数据源,也将用在jndi的名称,%DS_NAME%的数据源的jndi 名称为/%DS_NAME%,比如上面配置的DataSource将可以通过 ctx.lookup(“/TestMysqlDataSource”)和ctx.lookup(“/TestXAMysqlDataSource”)得到。
    对于不同的数据源,URL的写法不一样,如下:
    mysql: jdbc:mysql://localhost/test
    Oracle:     jdbc:oracle:thin:@localhost:1521:yang
    DB2: jdbc:db2://localhost;databaseName=SAMPLE
    MSSQL Server: (必须设置selectMethod =cursor) jdbc:microsoft:sqlserver://localhost:1433; selectMethod =cursor;databaseName=db
    注意:要使MSSQL Server支持XA JDBC驱动,需要在服务器上运行一段存储过程,具体操作请从Microsoft网站下载MSSQL Server JDBC驱动后阅读其文档。

4.5传输协议
    JFox目前支持LOCAL、JRMP、JRMP_SSL、SOAP协议,可以见 org.jfox.ejb.meta.Protocal.
    JFox使用了协议后决的调用方式,在获得EJB Home的时候,并没有给接下来的调用绑定协议,直到create EJB对象的时候,才绑定一个确切的协议,我们叫这个为协议后决的调用方式,这种调用方式可以让我们自由的选择和切换ejb调用的协议。默认情况下,远程客户调用应用服务器中的EJB组件,将使用JRMP协议,而同一个应用服务器中间的EJB之间的调用,将使用之间的Java调用方式(LOCAL调用),提高执行速度。但是在某些特定的情况下,比如希望EJB之间的调用也采用JRMP进行远程调用(虽然这种情况很少),或者希望采用JRMP_SSL来实现对EJB的安全调用,或者希望采用SOAP调用协议来访问防火墙之后的应用,这个时候,就需要强行转换调用协议。
转换调用协议有两种方法:
1.通过定义Meta类来改变ejb的默认访问协议,这在上面的EJBMeta一节以及讲到。
2. 在程序中动态的切换协议,看下面的代码节选,完整的代码见:
%JFOX_HOME%/TestEJB/src/org/jfox/examples/ejb/protocol/TestProtocolBean.java

Code:
public class TestProtocolBean implements SessionBean {

public TestProtocolBean() {
}

public void setSessionContext(SessionContext sessionContext) throws EJBException {
}

public void ejbRemove() throws EJBException {
}

public void ejbActivate() throws EJBException {
}

public void ejbPassivate() throws EJBException {
}

public void ejbCreate() throws CreateException {
}

public String invokeByJRMP() {
try {
TestStatelessHome home = getTestStatelessOnePhase();
((ExtendedEJBHome) home).useProtocol(Protocol.JRMP);
TestStateless statelessEjb = home.create();
String voice = statelessEjb.getVoice();
return voice;

}
catch(Exception e) {
e.printStackTrace();
throw new EJBException(e);
}
}

public String invokeByJRMP_SSL() {
try {
TestStatelessHome home = getTestStatelessOnePhase();
((ExtendedEJBHome) home).useProtocol(Protocol.JRMP_SSL);
TestStateless statelessEjb = home.create();
String voice = statelessEjb.getVoice();
return voice;

}
catch(Exception e) {
e.printStackTrace();
throw new EJBException(e);
}

}

public String invokeBySOAP() {
try {
TestStatelessHome home = getTestStatelessTwoPhase();
((ExtendedEJBHome) home).useProtocol(Protocol.SOAP);
TestStateless statelessEjb = home.create();
String voice = statelessEjb.getVoice();
return voice;

}
catch(Exception e) {
e.printStackTrace();
throw new EJBException(e);
}

}

public String invokeByLOCAL() {
try {
TestStatelessHome home = getTestStatelessTwoPhase();
((ExtendedEJBHome) home).useProtocol(Protocol.LOCAL);
TestStateless statelessEjb = home.create();
String voice = statelessEjb.getVoice();
return voice;

}
catch(Exception e) {
e.printStackTrace();
throw new EJBException(e);
}

}

private static TestStatelessHome getTestStatelessOnePhase() throws Exception {
Context ctx = new InitialContext();
Object home = ctx.lookup("java:comp/env/ejb/TestStatelessEJB");
TestStatelessHome statelessHome =
(TestStatelessHome) javax.rmi.PortableRemoteObject.narrow(home, TestStatelessHome.class);
return statelessHome;
}

private static TestStatelessHome getTestStatelessTwoPhase() throws Exception {
Context initCtx = new InitialContext();
Context ctx = (Context) initCtx.lookup("java:comp/env");
Object home = ctx.lookup("ejb/TestStatelessEJB");
TestStatelessHome statelessHome =
(TestStatelessHome) javax.rmi.PortableRemoteObject.narrow(home, TestStatelessHome.class);
return statelessHome;
}
}


    ExtendedEJBHome.useProtocol() 支持的有效参数在Protocol类中定义,现在有 JRMP 和 LOCAL,JRMP_SSL,SOAP协议不区分大小写。因为JFox已经自动选择了最佳的调用协议,且强行转换调用协议超出了EJB规范的内容,所以我们希望您一般情况下不要这样做。

4.6 Web Container
    JFox集成Jetty做为Web Container,Jetty是知名的、开放的、被广泛使用的Web服务器。
    JFox支持web.xml 的 ejb-ref resource-ref resource-env-ref env-entry 等环境引用功能。

5.应用部署
    JFox支持动态部署 EAR、JAR、WAR模块,部署目录为%JFOX_HOME%/deploy,只要将上述三类型的模块拷贝至该目录,JFox会自动发现并部署。
    deploy 目录下已经有几个默认的ear包和war供用户参考,其中xmlrpc.war提供对SOAP协议的支持,如果用户需要使用SOAP协议,请不要删除该包。

5.1 EJB JAR部署
    JAR包的部署由JFox新生一个classloader直接装载。
5.2 WAR部署
    JFox首先将war包解压至于%JFOX_HOME/temp/webapps,然后交给jetty部署该目录,JFox classloader是该web module 的parent classloader。
    注意:1.如果你的Servlet采用默认的访问路径,那么该Servlet包必须由jetty的classloader来加载,也就是应该放到 WAR的lib或者classes目录下,不能放到JFox classloader的class path 中。2.%JFOX_HOME%/webapps目录用来以目录结构直接部署,在启动JFox是,WebContainer将加载该目录下的web应用,该目录下的war文件并不会自动部署

5.3 EAR部署
    EAR的部署涵盖的JAR部署和WAR部署。
    JFox首先该ear包解压到%JFOX_HOME/temp/$ear_file_name$下,然后重新组织目录结构,将ejb jar包都拷贝到解压后目录的apps下,war包解压到webapps目录下,公共的lib(由ear包中的MANIFEST.MF的CLASS-PATH来指定)移动到lib目录下,这样重新组织目录结构是为了让用户很方面就可以看到部署了哪些ejb jar,web war和使用了哪些lib。然后JFox会生成了一个classloader,这些classloader装载lib下面的类,然后状态ejb jar,最后jetty使用该classloader作为parent classloader新生一个web classloader来部署 webapps 下面的web 模块,这样web 中的servlet应用和ejb可以共享lib下的库。部署器也会分析 web 模块的web.xml文件,绑定其中定义的 resource-ref resource-env-ref ejb-ref env-entry等资源引用信息。
    更多的内容可以参考%JFOX_HOME/examples 的代码,也可以参考%JFOX_HOME/deploy下的包。

6.运行环境
    本章将详细介绍JFox 系统的运行环境。主要包含硬件主机的配置和支撑软件的安装需求。
硬件配置
服务器:PC Server 或 SUN Ultra 10
CPU:PIII 500 或与其相当的处理器
内存:256M以上
硬盘:1GB以上
支撑软件
操作系统:Linux, Windows 98, 2000, XP, Solaris8或Solaris2.6