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

JFox用户指南


(by huihoo.org JFox项目组young_yy@hotmail.com)

概览

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

特性

1.符合 ejb2.0 SessionBean规范
暂不支持EntityBean,推荐采用SessionBean + DAO/OR 来进行数据持久化
2.采用增强的MX 内核
该MX内核对原JMX功能做了简化,而在开发逻辑和可扩展性和可维护性方面做了增强,是一个为开发中间件产品量身定做的MX内核
3.完全支持JTA 1.01b规范,支持2pc
4.支持数据源
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.采用优化的调用链模型,提高服务端执行速度

安装

1.安装JDK,建议1.4以上版本,并设置%JAVA_HOME%环境变量;
2.从http://sourceforge.net/projects/jfox下载jfox最新版本,解压至某个目录,假定为%JFOX_HOME%
3.在%JFOX_HOME%/bin下,运行jfox-start.bat启动JFox
4.运行jfox-test-stateless.bat和jfox-test-stateful.bat测试程序,如果没有抛出异常,表明测试成功。

第一个EJB

假定我们要开发一个叫做Hello的EJB,步骤如下,具体代码参考%JFOX_HOME%/examples/src
1.定义Remote接口,Hello.java
2.定义Home接口,HelloHome.java
3.实现Hello EJB,HelloBean.java
4.实现一个Meta类,HelloMeta.java
5.使用jar打包,拷贝至%JFOX_HOME%deploy下,发布完成
在这些步骤中,前3步都和经典的ejb开发相同,第四步属于JFox的特色,通过一个Meta类来替代ejb-jar.xml,从而简化EJB的开发,我们来看看HelloMeta.java的内容

package org.jfox.ejb.examples.stateless;
import org.jfox.ejb.meta.EJBMeta;
import org.jfox.ejb.meta.EJBMetaInfo;
import org.jfox.ejb.meta.EJBMetaHelper;

public class HelloMeta implements EJBMeta{
  public EJBMetaInfo getEJBMetaInfo() {
    // 简单的Stateless SessionBean Meta 设置
    return EJBMetaHelper.simpleStatelessMetaInfo(HelloHome.class);
  }
}


可以看到,HelloMeta.java 实现了EJBMeta接口,并实现了一个方法 getEJBMetaInfo(),该方法返回EJBMetaHelper.simpleStatelessMetaInfo(HelloHome.class); 该方法使用HelloHome.class作为参数,simpleStatelessMetaInfo将返回一个简单配置的Stateless EJBMeta,对这个EJBMeta的详细描述如下:

	Home => HelloHome
	Remote => Hello
	EJB Name=>Hello
	Bean Class =>HelloBean
	Jndi => HelloHome.class.getName().replace('.','/')  (如:/org/jfox/examples/stateless/HelloHome)
	Transaction=>Required
	Description => ejb Hello


HelloMeta完成之后,一个完整的EJB组件也就完成了,使用jar打包并拷贝到%JFOX_HOME%deploy下即可发布。 如果需要把Hello变成一个Stateful的SessionBean(当然这种情况很少),只要修改HelloMeta类,把EJBMetaHelper.simpleStatelessMetaInfo(HelloHome.class) 改成EJBMetaHelper.simpleStatefulMetaInfo(HelloHome.class),再次编译打包发布即可,是不是很简单呢。

事务处理

为了保证jfox的服务端执行效率和业务安全,jfox实现了一个性能优异的事务处理器,通过和jboss进行对比测试,jfox tm 提供了更好的性能,详情参见:%JFOX_HOME%/docs/jfoxtm-test.doc

数据源

jfox 数据源支持所有提供XA规范驱动程序的数据库,现在绝大多数数据库都已提供XA驱动,包括Microsoft SQL Server,SQL Server的jdbc驱动可以在Microsoft的网站上下载到。
要添加一个数据源,可以在%JFOX_HOME%/conf/jfox-conf.xml中增加如下MX元素:

  <MX CLASSNAME="org.huihoo.jfox.mx.TxDataSourceServiceMX"

OBJECTNAME=":comp=TxDataSourceServiceMX">

    <METHOD NAME="mx_setDsName">

      <ARG TYPE="java.lang.String">TestDataSource</ARG>

    </METHOD>

    <METHOD NAME="mx_setDbUrl">

      <ARG TYPE="java.lang.String">jdbc:mysql://localhost/test</ARG>

    </METHOD>

    <METHOD NAME="mx_setUser">

      <ARG TYPE="java.lang.String">root</ARG>

    </METHOD>

    <METHOD NAME="mx_setPassword">

      <ARG TYPE="java.lang.String"></ARG>

    </METHOD>

    <METHOD NAME="mx_start"></METHOD>

  </MX>


这将创立一个名为TestDataSource的Mysql数据源,数据源的名称不仅用来唯一的标志一个数据源,也将用在jndi的名称,%DS_NAME%的数据源的jndi 名称为/datasource/%DS_NAME%,比如上面配置的DataSource将可以通过 ctx.lookup("datasource/TestDataSource")得到。
对于不同的数据源,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驱动后阅读其文档。

EJBMeta的定制

用户也可以随意自定义EJBMetaInfo,参考Class org.jfox.ejb.examples.meta.HelloMeta,下面是其 getMetaInfo 的实现代码:


     public EJBMetaInfo getEJBMetaInfo() {
      // 自定义的Stateless SessionBean
       return EJBMetaHelper. createEJBMeta (
			"EJB Hello with custom meta info",       //description
                        "Hello",                            // ejb name
			"org.jfox.ejb.examples.meta.HelloHome", // Home class
                        "org.jfox.ejb.examples.meta.Hello",      // Remote class
                        "org.jfox.ejb.examples.meta.HelloBean",   // Bean class
                         SessionTxMetaInfo.not_support(),      // Transaction type
                        "org/jfox/ejb/examples/meta/HelloEJB",   // jndi name
                        false                               // is Stateful SessionBean
);
     }

通过提供完整的参数给EJBMetaHelper. createEJBMeta,定义了返回的EJBMetaInfo的内容,这使得可以适应那些EJB命名不规范或者希望自定义Transaction Type或者自定义jndi名称等等的EJB组件,使得布署EJB更加灵活。

另外值得注意的是,jfox是根据EJB jar 包中的Meta类来自动布署EJB组件的,所以请确保每个EJB都有一个Meta类与之对应,并且保证Meta类提供的信息是正确的,否则,你的EJB可能不能布署或者布署失败!

传输协议

JFox使用了协议后决的调用方式,在获得EJB Home的时候,并没有给接下来的调用绑定协议,知道进行EJB组件方法调用的时候,才绑定一个确切的协议,JFox暂时只支持JRMP 和 Local 两种协议,更多的协议如:SOAP HTTP IIOP等将在后来的版本中发布。

默认情况下,远程客户调用应用服务器中的EJB组件,将使用JRMP协议,而同一个应用服务器中间的EJB之间的调用,将使用之间的Java调用方式(LOCAL调用),提高执行速度。

但是在某些特定的情况下,比如希望EJB之间的调用也采用JRMP进行远程调用(虽然这种情况很少),这个时候,就需要强行转换调用协议,jfox提供了强行转换调用协议的接口。看下面的例子,完整的代码见:org.jfox.ejb.examples.protocol.HelloBean


 public String getWordByLocal() throws RemoteException {
    try {
      Context ctx = new InitialContext();
      Object obj = ctx.lookup("" + WorldHome.class.getName().replace('.','/'));
      WorldHome home = (WorldHome)javax.rmi.PortableRemoteObject.narrow(obj,WorldHome.class);
      // 可以省略,ejb 之间的调用默认将采用 Local 协议调用
((ExtendedEJBHome)home).useProtocol("LOCAL"); 
      World world = home.create();
      return world.getWord();
    }
    catch(Exception e){
      throw new RemoteException(e.getMessage(),e);
    }
  }

  public String getWordByJRMP() throws RemoteException {
    try {
      Context ctx = new InitialContext();
      Object obj = ctx.lookup("" + WorldHome.class.getName().replace('.','/'));
      WorldHome home = (WorldHome)javax.rmi.PortableRemoteObject.narrow(obj,WorldHome.class);
      ((ExtendedEJBHome)home).useProtocol("JRMP");  // EJB 之间的调用强行使用 JRMP 协议
      World world = home.create();
      return world.getWord();
    }
    catch(Exception e){
      throw new RemoteException(e.getMessage(),e);
    }
  }


现在ExtendedEJBHome.useProtocol() 的有效参数只有 JRMP 和 LOCAL,协议不区分大小写。

因为jfox已经自动选择了最佳的调用协议,且强行转换调用协议超出了EJB规范的内容,所以我们希望您一般情况下不要这样做。