我的Session Bean Container实现(3)

第三节 编写Stateless Session Bean container

 

在第二节我们编写了一个能够初始化Bean并调用Bean中方法的框架。这一节我们对框架略加修改来完成无状态的Session Bean容器。

 

我们来看看什么是无状态Session Bean

 

EJB规范中,无状态Session Bean不维护客户的会话状态。当客户调用无状态Session Bean的方法时,bean实例的成员变量可能包含bean的状态,但是这个状态到bean方法调用执行完后就不再被保存了。除了bean的方法被调用期间,所有无状态Session Bean对象都是等价的,这允许EJB container可以将没有方法被调用的无状态Session Bean指派给任一客户。

 

这就是说,当客户A调用完无状态的Session Bean 的方法后,客户B可以立即调用这个Session Bean 的任一方法。为什么要这样呢?这是为了节省CPU资源和内存资源。

 

我们将在一给定的时间内没有任何方法被调用的Bean对象,称为在这段时间内空闲的对象,简称空闲对象。

 

既然无状态Session Bean不保留方法执行后的状态,那么Home接口中的create()方法就没有必要带参数创建Bean(EJB规范中也规定无状态Session beancreate()方法不能带参数)

 

实质上对无状态的Session Bean而言,EJB规范中也没有必要规定ejbCreate()方法了,因为ejbCreate()方法同其他的方法相比没有什么特殊性了。

 

现在我们明白了无状态Session Bean的含义了,那么在第二节中调用Home接口的create()方法创建的Bean对象可以放到一个Bean Pool中。如果第二个客户要求创建Bean对象并且刚好Bean Pool中有空闲的Bean对象,我们就不用真的创建Bean对象了而直接利用Bean Pool中的空闲Bean对象。为了管理空闲对象,我们设计了BeanPool类。BeanPool类的定义如下:

 

package ejb.sessionbean.stateless;

 

package ejb.sessionbean.stateless;

 

import java.util.ArrayList;

import java.util.List;

 

public class BeanPool {

 

               private Class beanClass_;

               private List freeBeans_ = new ArrayList();

              

               public BeanPool( Class beanClass ) {

                              beanClass_ = beanClass;

               }

              

               public Object getBean() throws InstantiationException, IllegalAccessException{

                              synchronized(freeBeans_) {

                                             if( freeBeans_.isEmpty() )

                                                            return beanClass_.newInstance();

                                             else return freeBeans_.remove( 0 );

                              }

                             

               }

              

               public void freeBean( Object bean ) {

                              synchronized(freeBeans_) {

                                             freeBeans_.add( bean );

                              }                           

               }

}

 

BeanPool类中,getBean()方法获得一个空闲的Bean对象,如果没有空闲的bean对象,就创建一个新的Bean对象并返回该对象。如果有空闲的Bean对象,就返回一个空闲的Bean对象。由于getBean()方法有了创建Bean对象的能力,原来的Home接口和Remote接口可以只调用BeanPool.getBean()方法。freeBean()方法将一个bean对象标示为空闲对象。

 

现在我们来看在RemoteInvocationHandler中如何调用BeanPool.getBean()BeanPool.freeBean()来创建Bean对象或获取空闲Bean对象和释放执行完某一方法后的Bean对象。

 

package ejb.sessionbean.stateless;

 

import java.lang.reflect.*;

import java.rmi.*;

 

public class RemoteInvocationHandler implements InvocationHandler {

              

               private Class beanClass_;

               private BeanPool beanPool_;

              

               public RemoteInvocationHandler ( Class beanClass, BeanPool beanPool ) {

                              beanClass_ = beanClass;

                              beanPool_ = beanPool;

               }

              

               public Object invoke( Object proxy,             

                                             Method method,

                                             Object[] args ) throws Throwable{

                              return invoke( proxy, method.getName(), method.getParameterTypes(), args );

               }

              

               public Object invoke( Object proxy,

                                             String methodName,

                                             Class[] paramTypes,

                                             Object[] args ) throws Throwable{

                              Object bean = null;

                              Object ret = null;

                              try {

                                             Method m = beanClass_.getMethod( methodName, paramTypes );

 

                                             bean = beanPool_.getBean();

                                             ret = m.invoke( bean, args );

                              }catch( Exception ex ) {

                                             throw new RemoteException( "Fail to find or execute method:" + methodName, ex );

                              }finally {

                                             if( bean != null )

                                                            beanPool_.freeBean( bean );

                                             return ret;

                              }            

               }

}

 

在调用ret = m.invoke( bean, args )之前从beanPool_中创建一Bean对象或获取一空闲Bean对象。在ret = m.invoke( bean, args )之后,将bean对象标示为空闲对象放入beanPool_中。

 

这个RemoteInvocationHandler 同第二节的有些不同:

  • 构造函数RemoteInvocationHandler (String beanClassName) RemoteInvocationHandler( Class beanClass )取代。这是为了以后可以支持用户指定的ClassLoader
  • 增加了如下方法

public Object invoke( Object proxy,

                                    String methodName,

                                    Class[] paramTypes,

                       Object[] args ) throws Throwable

增加这个方法的目的是为了将ejbCreate方法看作为与其他Bean方法一样的方法。

 

现在让我们看看无状态Session BeanHomeInvocationHandler是如何定义的,下面是是HomeInvocationHandler的定义:

 

package ejb.sessionbean.stateless;

 

import java.lang.reflect.*;

import java.rmi.*;

 

public class HomeInvocationHandler implements InvocationHandler {

               private Class remoteClass_;

               private Class beanClass_;

               private BeanPool beanPool_;

              

               public HomeInvocationHandler( Class remoteClass, Class beanClass ) {

                              remoteClass_ = remoteClass;

                              beanClass_ = beanClass;

                              beanPool_ = new BeanPool( beanClass );

               }

              

               public Object invoke( Object proxy,             

                                             Method method,

                                             Object[] args ) throws Throwable   

               {

                              try {

                                             RemoteInvocationHandler handler = new RemoteInvocationHandler( beanClass_, beanPool_ );

                                             Object proxyObj = Proxy.newProxyInstance( proxy.getClass().getClassLoader(),

                                                                                                                                                                                                   new Class[]{remoteClass_ },

                                                                                                                                                                                                   handler );     

                                             if( method.getName().equals( "create" ) ) {                                

                                                            handler.invoke( proxyObj, "ejbCreate", method.getParameterTypes(), args );

                                             }

                                             return proxyObj;

                              }catch( Exception ex ) {

                                             throw new RemoteException( "fail to call the method:" + method, ex );

                              }

               }

}

 

在这个HomeInvocationHandlerinvoke()方法中不再显示创建Bean对象了,Bean对象的创建已经RemoteInvocationHandler中隐含的实现了。由于要能够支持用户指定的ClassLoader,在HomeInvocationHandler的构造函数变为了:

 

public HomeInvocationHandler( Class remoteClass, Class beanClass )

 

当用户每调用一次Home接口的create()方法时,HomeInvocationHandler都要创建RemoteInvocationHandlerRemote接口的代理对象。如果在HomeInvocationHandler中只创建一个RemoteInvocationHandlerRemote接口的代理对象也能满足EJB中无状态Session bean的要求就好了。如果只有一个线程调用创建的Remote接口的代理对象的方法,不用改HomeInvocationHandler的实现就可以满足要求。现在我们主要考虑多个线程同时调用Remote接口的代理对象的方法的情况。当调用RemoteInvocationHandler.invoke()方法时,Bean对象可以从BeanPool中安全的存取(因为存取Bean时要取得同步锁),所以即使只有一个RemoteInvocationHandler和一个Remote接口的代理对象也能支持多线程。那么只需要将RemoteInvocationHandlerRemote接口的代理对象的创建移入HomeInvocationHandler的构造函数即可:

 

package ejb.sessionbean.stateless;

 

import java.lang.reflect.*;

import java.rmi.*;

 

public class HomeInvocationHandler implements InvocationHandler {

               private Class remoteClass_;

               private Class beanClass_;

               private BeanPool beanPool_;

               private Object proxyObj_;

               private RemoteInvocationHandler handler_;

              

               public HomeInvocationHandler( Class remoteClass, Class beanClass ) {

                              remoteClass_ = remoteClass;

                              beanClass_ = beanClass;

                              beanPool_ = new BeanPool( beanClass );

                              handler_ = new RemoteInvocationHandler( beanClass_, beanPool_ );

                              proxyObj_ = Proxy.newProxyInstance( remoteClass_.getClassLoader(),

                                                                                                                                                                                                   new Class[]{remoteClass_ },

                                                                                                                                                                                                   handler_ );     

               }

              

               public Object invoke( Object proxy,             

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                              try {

                                             if( method.getName().equals( "create" ) ) {                                

                                                            handler_.invoke( proxyObj_, "ejbCreate", method.getParameterTypes(), args );

                                             }

                                             return proxyObj_;

                              }catch( Exception ex ) {

                                             throw new RemoteException( "fail to call the method:" + method, ex );

                              }

               }

              

}

 

HomeProxyFactory可以定义为:

 

package ejb.sessionbean.stateless;

 

import java.lang.reflect.*;

 

public class HomeProxyFactory

{

               public static Object createHomeProxy( String homeClassName,

                                                                           String remoteClassName,

                                                                           String beanClassName ) throws ClassNotFoundException {

                              return createHomeProxy( Class.forName( homeClassName ),

                                                                                                                        Class.forName( remoteClassName ),

                                                                                                                        Class.forName( beanClassName ) );

               }

 

               public static Object createHomeProxy( ClassLoader cl,

                                                                           String homeClassName,

                                                                           String remoteClassName,

                                                                           String beanClassName ) throws ClassNotFoundException {

                              return createHomeProxy( Class.forName( homeClassName, true, cl ),

                                                                                                                        Class.forName( remoteClassName, true, cl ),

                                                                                                                        Class.forName( beanClassName, true, cl ) );

               }

              

               public static Object createHomeProxy( Class homeClass,

                                                                           Class remoteClass,

                                                                           Class beanClass ) {

                              try {

                                             InvocationHandler handler = new HomeInvocationHandler ( remoteClass, beanClass );                        

                                                                                         

                                             return Proxy.newProxyInstance( homeClass.getClassLoader(), new Class[]{homeClass}, handler );                      

                              }catch( Exception ex ) {

                                             throw new RuntimeException( ex );

                              }            

               }

 

}

 

 

现在,我们可以创建一个与无状态Session Bean行为的Container了:

package ejb.sessionbean.stateless;

 

public class Container {

 

               private Object homeObj_;

              

               public Container(String homeClassName,

                                             String remoteClassName,

                                             String beanClassName ) throws ClassNotFoundException  {

                              this( ClassLoader.getSystemClassLoader(),

                                                            homeClassName,

                                                            remoteClassName,

                                                            beanClassName);

               }

 

               public Container( ClassLoader cl,

                                             String homeClassName,

                                             String remoteClassName,

                                             String beanClassName,) throws ClassNotFoundException  {

                              this( Class.forName( homeClassName, true, cl ),

                                               Class.forName( remoteClassName, true, cl ),

                                               Class.forName( beanClassName, true, cl ) );

               }

              

               public Container( Class homeClass,

                                             Class remoteClass,

                                             Class beanClass ) throws ClassNotFoundException  {

                              homeObj_ = HomeProxyFactory.createHomeProxy( homeClass,

                                                                                                                        remoteClass,

                                                                                                                        beanClass );

               }

 

               public Object getHomeProxy() {

                              return homeObj_;

               }

}

 

这个Container只能从本地调用,还不具备从网络调用的能力。如果将创建的Home接口代理和Remote接口代理export到网络上去,就能从网络上调用了。EJB container使用的是RMI-IIOP网络协议,我们看看如何将Home接口代理和Remote接口代理exportRMI-IIOP网络上去。最简单的办法是创建Home接口代理和Remote接口代理的时候将它们export出去,因此我们需要修改HomeInvocationHandler类和Container类。

 

修改后的HomeInvocationHandler如下:

 

package ejb.sessionbean.stateless;

 

import java.rmi.*;

import javax.rmi.PortableRemoteObject;

import java.lang.reflect.*;

 

public class HomeInvocationHandler implements InvocationHandler {

               private Class remoteClass_;

               private Class beanClass_;

               private BeanPool beanPool_;

               private Object proxyObj_;

               private RemoteInvocationHandler handler_;

                             

               public HomeInvocationHandler( Class remoteClass, Class beanClass ) throws RemoteException {

                              remoteClass_ = remoteClass;

                              beanClass_ = beanClass;

                              beanPool_ = new BeanPool( beanClass );

                              handler_ = new RemoteInvocationHandler( beanClass_, beanPool_ );

                              proxyObj_ = Proxy.newProxyInstance( remoteClass_.getClassLoader(),

                                                                                          new Class[]{remoteClass_ },

                                                                                          handler_ );

                              PortableRemoteObject.exportObject( (Remote)proxyObj_ );                                                                                                                                                                                            

               }

              

               public Object invoke( Object proxy,             

                                             Method method,

                                             Object[] args ) throws Throwable

               {

                              try {

                                             if( method.getName().equals( "create" ) ) {                                

                                                            handler_.invoke( proxyObj_, "ejbCreate", method.getParameterTypes(), args );

                                             }

                                             return proxyObj_;

                              }catch( Exception ex ) {

                                             throw new RemoteException( "fail to call the method:" + method, ex );

                              }

               }

              

}

 

新的HomeInvocationHandler只增加了PortableRemoteObject.exportObject( (Remote)proxyObj_ ); 这条语句是将创建的Remote接口的代理对象export出去,网络客户端可以通过网络存取创建的Remote接口代理。

 

下面是修改后的Container类:

 

package ejb.sessionbean.stateless;

 

import java.rmi.Remote;

import java.rmi.RemoteException;

import javax.rmi.PortableRemoteObject;

import javax.naming.Context;

import javax.naming.NamingException;

import javax.naming.CompositeName;

 

public class Container {

 

               private Object homeObj_;

              

               public Container(String homeClassName,

                                                                           String remoteClassName,

                                                                           String beanClassName,

                                                                           String bindName,

                                                                           Context ctx ) throws RemoteException, NamingException, ClassNotFoundException  {

                              this( ClassLoader.getSystemClassLoader(),

                                                            homeClassName,

                                                            remoteClassName,

                                                            beanClassName,

                                                            bindName,

                                                            ctx );

               }

 

               public Container( ClassLoader cl,

                                                                           String homeClassName,

                                                                           String remoteClassName,

                                                                           String beanClassName,

                                                                           String bindName,

                                                                           Context ctx ) throws RemoteException, NamingException, ClassNotFoundException  {

                              this( Class.forName( homeClassName, true, cl ),

                                               Class.forName( remoteClassName, true, cl ),

                                               Class.forName( beanClassName, true, cl ),

                                               bindName,

                                               ctx );

               }

              

               public Container( Class homeClass,

                                                                           Class remoteClass,

                                                                           Class beanClass,

                                                                           String bindName,

                                                                           Context ctx ) throws RemoteException, NamingException, ClassNotFoundException  {

                              homeObj_ = HomeProxyFactory.createHomeProxy( homeClass,

                                                                                                                                                                     remoteClass,

                                                                                                                                                                     beanClass );

                              PortableRemoteObject.exportObject( (Remote)homeObj_ );

                              homeObj_ =        PortableRemoteObject.toStub( (Remote)homeObj_  );

                              rebind( ctx, bindName, homeObj_ );

               }

 

               public Object getHomeProxy() {

                              return homeObj_;

               }

 

               private static void rebind( Context ctx, String name, Object obj ) throws NamingException {

                              CompositeName compName = new CompositeName( name );

                             

                              if( compName.size() == 1 )

                                             ctx.rebind( name, obj );

                              else {

                                             int i = 0, n = compName.size() - 1;

                                             for( ; i < n; i++ ) {

                                                            Context localCtx = null;

                                                           

                                                            try {

                                                                           localCtx = (Context)ctx.lookup( compName.get( i ) );

                                                            }catch( Exception ex ) {

                                                            }

                                                            if( localCtx != null )

                                                                           ctx = localCtx;

                                                            else ctx = ctx.createSubcontext( compName.get( i ) );

                                                           

                                             }

                                             ctx.rebind( compName.get( n ), obj );

                              }

               }

              

}

 

修改后的Container增加了如下语句:

 

                        PortableRemoteObject.exportObject( (Remote)homeObj_ );

                        homeObj_ =             PortableRemoteObject.toStub( (Remote)homeObj_  );

                        rebind( ctx, bindName, homeObj_ );

 

并且每一个构造函数都增加了参数bindNamectx,这两个参数是为了将Home接口代理和Remote接口代理export到网络,客户端可以通过网络调用我们的Home接口代理和Remote接口代理。

 

现在我们的无状态Session Bean已经完成了。我们的无状态Session Bean能否工作呢,现在就让我们写一个测试程序,测试一下。

 

我们以J2EE教程的Converter作为测试用例:

 

ConvertHome接口定义为:

 

package converter;

 

import java.rmi.RemoteException;

 

 

public interface ConverterHome extends java.rmi.Remote {

    Converter create() throws RemoteException;

}

 

ConvertRemote接口定义为:

 

package converter;

 

import java.rmi.RemoteException;

import java.math.*;

 

 

public interface Converter extends java.rmi.Remote {

    public BigDecimal dollarToYen(BigDecimal dollars) throws java.rmi.RemoteException;

 

    public BigDecimal yenToEuro(BigDecimal yen) throws java.rmi.RemoteException;

}

 

ConvertBean定义如下:

 

package converter;

 

import java.rmi.RemoteException;

import java.math.*;

 

 

public class ConverterBean  {

    BigDecimal yenRate = new BigDecimal("121.6000");

    BigDecimal euroRate = new BigDecimal("0.0077");

 

    public ConverterBean() {

    }

 

    public BigDecimal dollarToYen(BigDecimal dollars) {

        BigDecimal result = dollars.multiply(yenRate);

 

        return result.setScale(2, BigDecimal.ROUND_UP);

    }

 

    public BigDecimal yenToEuro(BigDecimal yen) {

        BigDecimal result = yen.multiply(euroRate);

 

        return result.setScale(2, BigDecimal.ROUND_UP);

    }

 

    public void ejbCreate() {

    }

 

    public void ejbRemove() {

    }

 

    public void ejbActivate() {

    }

 

    public void ejbPassivate() {

    }

}

 

可以看到上面我们定义的Home接口、Remote接口、Bean的定义与EJB2.X的要求有些差别,没有从EJB2.X规范中的EJBHomeEJBObjectSessionBean接口继承。其实,EJB2.X规范中的EJBHome接口、EJBObject接口、SessionBean接口主要是为开发EJB Container的开发者所使用,不是为了给最终使用EJB Container的开发者所使用。正是这个原因,将EJB Container开发者所要使用的接口暴露给最终开发者使EJB2.X遭受到了许多批评。

 

我们定义了Home接口,Remote接口和ConvertBean,但是谁来初始化我们的Container呢?下面写了一个十分简单的Server来帮我们初始化Container:

 

import ejb.sessionbean.stateless.*;

import javax.naming.*;

 

public class Server {

               public static void main( String[] args ) {

                              try {

                                             InitialContext initialNamingContext = new InitialContext();

                                             Container container = new Container( "converter.ConverterHome",

                                                                                                         "converter.Converter",

                                                                                                         "converter.ConverterBean",

                                                                                                         "java:comp/env/ejb/SimpleConverter",

                                                                                                         initialNamingContext );

                              }catch( Exception ex ) {

                                             ex.printStackTrace();

                              }

               }            

}

 

 

Convert client的代码如下:

import converter.Converter;

import converter.ConverterHome;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.rmi.PortableRemoteObject;

import java.math.BigDecimal;

 

 

public class ConverterClient {

    public static void main(String[] args) {

        try {

            Context initial = new InitialContext();

            Context myEnv = (Context) initial.lookup("java:comp/env");

            Object objref = myEnv.lookup("ejb/SimpleConverter");

 

            ConverterHome home =

                (ConverterHome) PortableRemoteObject.narrow(objref,

                    ConverterHome.class);

 

            Converter currencyConverter = home.create();

 

            BigDecimal param = new BigDecimal("100.00");

            BigDecimal amount = currencyConverter.dollarToYen(param);

 

            System.out.println(amount);

            amount = currencyConverter.yenToEuro(param);

            System.out.println(amount);

 

            System.exit(0);

        } catch (Exception ex) {

            System.err.println("Caught an unexpected exception!");

            ex.printStackTrace();

        }

    }

}

 

将上面的代码编译,就可以执行了,首先启动JNDI

 

orbd -ORBInitialPort 1060

 

然后启动我们的Server

 

java –classpath . -Dcom.sun.CORBA.ORBUseDynamicStub=true -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory -Djava.naming.provider.url=iiop://localhost:1060 Server

 

最后启动我们的ClientConverter

 

java –classpath . -Dcom.sun.CORBA.ORBUseDynamicStub=true -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory -Djava.naming.provider.url=iiop://localhost:1060 ConverterClient

 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值