4 Replies Latest reply on Sep 26, 2001 9:31 AM by jseaman

    How does JBoss do Stateless Session Beans?

      Hi guys.
      I'm very confused about how JBoss handles stateless session beans.
      I've investigated the spec, jboss doc, DEBUG level jboss trace, forums
      and mailing list.
      I've tried to structure my problem into:
      - what i believe i should be seeing/getting
      - what i'm doing
      - what i'm getting
      - questions
      - code

      I would very much appreciate any clear insight into this...anyone
      from the dev team out there who knows for sure?

      cheers
      oliver

      Stateless session beans (as i understand them)
      o these are created by the container by executing the
      following:
      - Class.newInstance()
      - setSessionContext()
      - ejbCreate()
      after which they enter the 'Method-Ready Pool' and
      start servicing client requests.

      o The ejbCreate() method is invoked only once in the
      lifecycle of the stateless session bean. Also when
      the client invokes create() on the EJB Home, it is
      not delegated to the bean instance.

      o When a client calls a method on the remote
      interface, the bean instance is associated with a
      EJBObject for the duration of the method after
      which it is disassociated from the EJBObject and
      returned to the 'Method-Ready Pool'
      (Instance swapping)

      o A stateless session bean instance should be able to
      service multiple clients in its lifetime. However i
      guess it is up to the containers strategy...ie
      whether he pools instances or creates a new one
      every time one is needed.

      Actions
      I have a very simple stateless session bean that has
      one method a client can invoke called execute().

      A simple test client does the lookup, cast to home,
      home.create and remote.execute() - for which i have
      included the trace

      The next test is the same except that the execute()
      method is invoked twice on the same remote interface.


      please see the JBoss trace.

      Questions
      Q0 - Why does jboss create a new bean instance for
      every single remote method invocation on that bean.

      Q1 - Immediately after the bean has serviced the
      execute() method the container calls
      setSessionContext() and ejbCreate(). Why?

      Q2 - Why is ejbRemove() never called? Is it because
      all the beans created by ejbCreate() are still in
      the 'method-ready pool' and the server has not
      decided to remove them yet? If so, then why are
      these pooled instances not used to service the
      client request?

      Q3 - If the container creates a new instance of the
      stateless session bean everytime one is needed,
      then surely we should see the ejbRemove()s
      corresponding to the ejbCreate()s.

      Q4 - So what does jboss do with stateless session
      beans?

      JBoss Trace

      <-- Start of Deployment -->
      [AutoDeployer] Auto deploy of file:/D:/JBoss-2.4.1/deploy/SimpleStateless.jar
      [J2EE Deployer Default] Deploy J2EE application: file:/D:/JBoss-2.4.1/deploy/SimpleStateless.jar
      [J2eeDeployer] Create application SimpleStateless.jar
      [J2eeDeployer] install EJB module SimpleStateless.jar
      [Container factory] Deploying:file:/D:/JBoss-2.4.1/tmp/deploy/Default/SimpleStateless.jar/
      [Verifier] Verifying file:/D:/JBoss-2.4.1/tmp/deploy/Default/SimpleStateless.jar/ejb1010.jar
      [Verifier] SimpleStatelessBean: Verified.
      [Container factory] Deploying SimpleStatelessBean
      [Container factory] Container Invoker RMI Port='4444'
      [Container factory] Container Invoker Client SocketFactory='Default'
      [Container factory] Container Invoker Server SocketFactory='Default'
      [Container factory] Container Invoker Server SocketAddr='Default'
      [Container factory] Container Invoker Optimize='true'
      [Container factory] Begin java:comp/env for EJB: SimpleStatelessBean
      [Container factory] TCL: java.net.URLClassLoader@4af083
      [Container factory] End java:comp/env for EJB: SimpleStatelessBean
      [Container factory] Mapped Container method remove HASH -1842617161
      [Container factory] Mapped Container method getEJBHome HASH -993218923
      [Container factory] Mapped Container method getHandle HASH 1182305581
      [Container factory] Mapped Container method getPrimaryKey HASH -131865408
      [Container factory] Mapped Container method isIdentical HASH 285457048
      [Container factory] Mapped execute 1868813453to public void SimpleStatelessEJB.execute(int)
      [Container factory] Mapping remove
      [Container factory] Mapping remove
      [Container factory] Mapping getEJBMetaData
      [Container factory] Mapping getHomeHandle
      [Container factory] Mapping create
      [Container factory] JRMP 1.3 CI initialized
      [Container factory] Bound SimpleStatelessBean to SimpleStatelessBean
      [ContainerManagement] Initializing
      [ContainerManagement] Initialized
      [ContainerManagement] Starting
      [ContainerManagement] Started
      [Container factory] Deployed application: file:/D:/JBoss-2.4.1/tmp/deploy/Default/SimpleStateless.jar/
      [J2EE Deployer Default] J2EE application: file:/D:/JBoss-2.4.1/deploy/SimpleStateless.jar is deployed.
      <-- End of Deployment -->

      <-- Start of test client run (SINGLE call to remote interface method execute()) -->
      [Default] SimpleStatelessEJB:setSessionContext
      [Default] SimpleStatelessEJB:ejbCreate - EJBObject associated with this instance:
      [Default] id=5691336
      [Default]
      [Default] SimpleStatelessEJB:execute - param=1, test=1000893772805
      [Default] SimpleStatelessEJB:setSessionContext
      [Default] SimpleStatelessEJB:ejbCreate - EJBObject associated with this instance:
      [Default] id=5691336
      [Default]
      <-- End of test client run -->

      <-- Start of test client run (TWO calls to remote interface method execute()) -->
      [Default] SimpleStatelessEJB:execute - param=1, test=1000893772865
      [Default] SimpleStatelessEJB:setSessionContext
      [Default] SimpleStatelessEJB:ejbCreate - EJBObject associated with this instance:
      [Default] id=5691336
      [Default]
      [Default] SimpleStatelessEJB:execute - param=2, test=1000894153943
      [Default] SimpleStatelessEJB:setSessionContext
      [Default] SimpleStatelessEJB:ejbCreate - EJBObject associated with this instance:
      [Default] id=5691336
      [Default]
      <-- End of test client run -->


      CODE

      <-REMOTE------------------------------------------------------------->
      import javax.ejb.EJBObject;
      import java.rmi.RemoteException;
      
      public interface SimpleStateless extends EJBObject
      {
       public void execute(int aParam) throws RemoteException;
      }
      <-HOME--------------------------------------------------------------->
      import javax.ejb.EJBHome;
      import javax.ejb.CreateException;
      import java.rmi.RemoteException;
      
      public interface SimpleStatelessHome extends EJBHome
      {
       public SimpleStateless create() throws CreateException, RemoteException;
      }
      <-BEAN--------------------------------------------------------------->
      import javax.ejb.EJBObject;
      import javax.ejb.SessionBean;
      import javax.ejb.SessionContext;
      import javax.ejb.EJBException;
      import java.rmi.RemoteException;
      
      public class SimpleStatelessEJB implements SessionBean
      {
       //===========================================================================
       // Start SimpleStateless implementation
       //===========================================================================
       public void execute(int aParam)
       {
       System.out.println(CNAME + ":execute - param="+aParam + ", test="+test);
       }
       //===========================================================================
       // End SimpleStateless implementation
       //===========================================================================
      
       //===========================================================================
       // Start SimpleStatelessHome implementation
       //===========================================================================
       public void ejbCreate()
       {
       // init a member variable which will prove to us a new stateless session
       // bean has been moved a 'does not exist' to 'method-ready pool' state
       test = ""+System.currentTimeMillis();
      
       // have a look at who is servicing us
       try
       {
       EJBObject obj = ejbContext.getEJBObject();
       System.out.print(CNAME + ":ejbCreate - EJBObject associated with this instance:");
       System.out.print(" id="+obj.hashCode());
      
       if (lastEJBObject != null);
       {
       //System.out.print(" isIdentical to last="+lastEJBObject.isIdentical(obj));
       }
      
       System.out.println();
       lastEJBObject = obj;
       }
       catch (Exception e)
       {
       System.out.println(CNAME+":"+e);
       throw new EJBException(e);
       }
       }
       //===========================================================================
       // End SimpleStatelessHome implementation
       //===========================================================================
      
       //===========================================================================
       // Start EJB callback methods
       //===========================================================================
      
       public void ejbActivate() {} // not used
       public void ejbPassivate() {} // not used
      
       public void ejbRemove()
       {
       System.out.println(CNAME + ":ejbRemove");
       }
      
       public void setSessionContext(SessionContext aContext)
       {
       System.out.println(CNAME + ":setSessionContext");
       ejbContext = aContext;
       }
      
       public SessionContext getEJBContext()
       {
       System.out.println(CNAME + ":getSessionContext");
       return (ejbContext);
       }
       //===========================================================================
       // End EJB callback methods
       //===========================================================================
      
       //===========================================================================
       // Member variables
       //===========================================================================
       private SessionContext ejbContext;
       private EJBObject lastEJBObject;
       private String test;
      
       //===========================================================================
       // Constants
       //===========================================================================
       private final String CNAME = this.getClass().getName();
      }
      <-CLIENT------------------------------------------------------------->
      import java.util.Properties;
      import javax.rmi.PortableRemoteObject;
      import javax.naming.*;
      
      class TestClient
      {
       public static void main(String[] args)
       {
       Properties env = new Properties();
       env.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
       env.setProperty("java.naming.provider.url", "localhost:1099");
       env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
      
       try
       {
       // Get a naming context
       InitialContext jndiContext = new InitialContext(env);
       System.out.println("Got context");
      
       // Get a reference to the Bean
       Object ref = jndiContext.lookup("SimpleStatelessBean");
       System.out.println("Got reference");
      
       // Get a reference from this to the Bean's Home interface
       SimpleStatelessHome home = (SimpleStatelessHome) PortableRemoteObject.narrow (ref, SimpleStatelessHome.class);
      
       // Create remote object from the Home interface
       SimpleStateless s = home.create();
      
       // call remote method
       s.execute(1);
       System.out.println("called execute 1");
       s.execute(2);
       System.out.println("called execute 2");
       }
       catch(Exception e)
       {
       e.printStackTrace();
       }
       }
      }
      <-DEPLOY DESCRIPTOR-------------------------------------------------->
      <?xml version="1.0"?>
      <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
      
      <ejb-jar>
       <display-name>SimpleStatelessSessionBean</display-name>
       <enterprise-beans>
       <session>
       <ejb-name>SimpleStatelessBean</ejb-name>
       <home>SimpleStatelessHome</home>
       <remote>SimpleStateless</remote>
       <ejb-class>SimpleStatelessEJB</ejb-class>
       <session-type>Stateless</session-type>
       <transaction-type>Container</transaction-type>
       </session>
       </enterprise-beans>
      
       <assembly-descriptor>
       <container-transaction>
       <method>
       <ejb-name>SimpleStatelessBean</ejb-name>
       <method-name>*</method-name>
       </method>
       <trans-attribute>Required</trans-attribute>
       </container-transaction>
       </assembly-descriptor>
      
      </ejb-jar>
      


      MISC
      ====
      Java version: 1.3.1,Sun Microsystems Inc.
      Java VM: Java HotSpot(TM) Server VM 1.3.1-b24,Sun Microsystems Inc.
      System: Windows NT 4.0,x86
      JBoss: 2.4.1


        • 1. Re: How does JBoss do Stateless Session Beans?
          bkhopper

          You don't specify what version of JBoss you're using, but I'm experiencing the exact same problem. Under JBoss 2.4.0 on Windows NT 4.0 SP6a, the EJBs are pooled and everything works marvelously. Under JBoss 2.4.1 (both with Sun JDK 1.3.1) a new EJB is created for each remote invocation, ejbRemove() is never called, and the EJBs do not seem to be pooled.

          If anyone has any insight, I'd sure love to hear it.

          • 2. Re: How does JBoss do Stateless Session Beans?

            Hiya, posting response i got on the mailing list...web-access problemes yesterday.

            ---

            For various reason, mainly the highly added complexity when synchronising
            entity beans, the pool has been modified by ... not pooling i.e. the
            pool.free method instead pushes a new fresh instance on the pool stack.
            Consequently, the current code will call ejbRemove *only* when your server
            is under high load and it needs to free instances that cannot fit on the
            stack size. In this case, discard(ctx) is called and make appropriate calls.

            But I agree that the code sould be modified to either:
            - really pool SLSB (and only SLSB)
            - or correctly call ejbRemove on the SLSB

            Any opinion?
            Cheers,
            Sacha
            P.S.: I forward this to JBoss-dev.

            • 3. Re: How does JBoss do Stateless Session Beans?

              Hi.

              I decided to do some more investigation and
              rolled back to JBoss 2.2.2 (from 2.4.1 for those
              who did'nt see the end of my original posting).

              I seem to be getting the expected behavior
              with 2.2.2. That is, Stateless Session bean
              instance are being re-used and ejbCreate() is not
              called everytime the client invokes a method on
              the remote interface. Please see the JBoss 2.2.2
              Trace below.

              I also modified my test client and ran four instances
              of it against both versions of JBoss for over 10,000
              requests each. As expected there is quite a difference
              in terms of performance between the two. Using
              my (dumb) simple calculation i rekon 2.4.1 is over
              40% slower than 2.2.2 when dealing with Stateless
              Session beans.

              I don't seem to be getting any responses.
              The last posting by Sacha seemed to be indirectly
              saying "Yes this is a Bug"...am i mistaken.

              Any thougths, solutions, guidance much appreciated.
              cheers
              oliver
              www.pogo-tech.com


              JBoss 2.2.2 Trace
              [Mail Service] Mail Service 'Mail' bound to java:/Mail
              [Mail Service] Started
              [Service Control] Started 22 services
              [Default] JBoss 2.2.2 Started in 0m:14s
              <-- End of Deployment -->

              <-- Start of test client run (TWO calls to remote interface method execute()) -->
              [SimpleStatelessBean] SimpleStatelessEJB:setSessionContext
              SimpleStatelessEJB:ejbCreate - EJBObject associated with this instance: id=6418088
              [SimpleStatelessBean] SimpleStatelessEJB:execute - param=1, test=1000994622379
              [SimpleStatelessBean] SimpleStatelessEJB:execute - param=1, test=1000994622379
              <-- End of test client run -->

              Client Trace
              JBOSS 2.4.1
              [java] called execute 1: avg time to execute 1 call = 34
              [java] called execute 1: avg time to execute 1 call = 30
              [java] called execute 1: avg time to execute 1 call = 33
              [java] called execute 1: avg time to execute 1 call = 33

              JBOSS 2.2.2
              [java] called execute 1: avg time to execute 1 call = 18
              [java] called execute 1: avg time to execute 1 call = 16
              [java] called execute 1: avg time to execute 1 call = 19
              [java] called execute 1: avg time to execute 1 call = 20

              ==> jboss 2.4.1 avg = 32.5
              ==> jboss 2.2.2 avg = 18.25

              CODE

              <snip>
              start = System.currentTimeMillis();
              while(true)
              {
               s.execute(1);
               end = System.currentTimeMillis();
               System.out.println("called execute 1: avg time to execute 1 call = " + (end - start)/count);
               count++;
              }
              <snip>
              


              MISC
              Java version: 1.3.1,Sun Microsystems Inc.
              Java VM: Java HotSpot(TM) Server VM 1.3.1-b24,Sun Microsystems Inc.
              System: Windows NT 4.0,x86
              JBoss: 2.4.1
              JBoss: 2.2.2


              • 4. Re: How does JBoss do Stateless Session Beans?
                jseaman

                Hello,

                Has there been any resolution to this - I am seeing the exact behavior. 2.4.1 is a lot slower than 2.2.2

                Thanks,

                Jeff