How does JBoss do Stateless Session Beans?
jardia Sep 19, 2001 12:19 PMHi 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