Conversations with Beans

jimlowrey's picture

There are a few things I've been trying to get straight about Beans and Conversations with JBoss Seam. To that end I worked out these examples.

1)Stateless bean with number counter

package org.jlowrey.examples.sessionbeans;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.ScopeType;
@Name("Stateless")//this put's this object in "el" scope in the xhtml page.
@Scope(ScopeType.STATELESS)
public class Stateless {
    private int count = 0;
    public int getCount()
    {
	    count++;
	    return count;
    }
}

And a page to access it.

<html>
Stateless has been called: <b>#{Stateless.getCount()}</b> times.
</html>

Hit refresh several times and as one would guess the count never goes above 1. The bean is stateless and should not keep state between uses. Is there a case where this bean could show a count more than one? An improper use of a Stateless EJB does it.

package org.jlowrey.examples.sessionbeans;
import org.jboss.seam.annotations.Name;
import javax.ejb.Stateless;
 
@Stateless //this annotation turns it into an Enterprise Java Bean.  This annotation also sent me 
           //on a wild goose chase as in my <a href="http://docs.jboss.com/seam/latest/reference/en/html/gettingstarted.html" title="http://docs.jboss.com/seam/latest/reference/en/html/gettingstarted.html">http://docs.jboss.com/seam/latest/reference/en/html/gettingstarted.html</a> I didn't specify an Ear file, I specified war.  Thus the
           //configuration I had couldn't use EJB's and this this one wouldn't be found.
@Name("StatelessEJB")
public class StatelessEJBBean implements StatelessEJB  //Also note EJB's have to implement an interface.  Some abstraction little guys like
                                                       //don't care much about, but have to adhere to.  This is not <a href="http://www.rubyonrails.org" title="http://www.rubyonrails.org">http://www.rubyonrails.org</a> Dorothy...
{
    private int count = 0;
    public int getCount()
    {
	    count++;
	    return count;
    }
}
 
package org.jlowrey.examples.sessionbeans;
import javax.ejb.Local;
@Local
public interface StatelessEJB {
 
    public int getCount();
}
and a page to view it with
<html>
Stateless has been called: <b>#{Stateless.getCount()}</b> times.
StatelessEJB has been called: <b>#{StatelessEJB.getCount()}</b> times.
</html>

I then hit refresh a few times.. wtf! StatelessEJB's counter is up to 5. Uh... as it turns out the container, in this case JBoss 4.x, is allowed to recycle Stateless EJB's as in use them over again, duh, it says it in the docs.

2) Stateful Session Beans in the session context. Next I tried some stateful session beans with the state being held in various contexts. First up a stateful session bean being held in the session context. Is it me, or do I hear the term session being used for stateful bean that has state in any context? Like a "session bean" in conversation context. Yes, I do. Here's quote from the docs "By default, stateful session beans are bound to the conversation context." That's confused me up until this point. So by session they mean any context that holds state. I guess I've confused HTTP session as a one size fits all session and not the fine grained variances they have, conversation, session, event, page, and so on. Anyway...

package org.jlowrey.examples.sessionbeans;
 
import org.jboss.seam.annotations.Name;
import javax.ejb.Stateless;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Scope;
 
@Name("Stateful")
@Scope(ScopeType.SESSION)
public class StatefulBean
{
    private int count = 0;
    public int getCount()
    {
	    count++;
	    return count;
    }
}

and code to display
<html xmlns="http://www.w3.org/1999/xhtml" >
<br/>
Stateless has been called: <b>#{Stateless.getCount()}</b> times.<br/>
StatelessEJB has been called: <b>#{StatelessEJB.getCount()}</b> times.<br/>
Statefull has been called: <b>#{Stateful.getCount()}</b> times.<br/>
</html>

This little bugger of course kept track of every click. An interesting thing to do was to open different tabs and windows to see how the different browsers propagate sessions. Opera kept session across all tabs and all windows (on windows) and IE kept session across tabs but on opening a new window the counter dropped back down to 1.

Ok, so far so good. Stateless beans keep track of nothing, stateless EJB's shouldn't be used to keep track off anything but will if used the wrong way, and stateful session beans in the session context. What about conversations?
3)Stateful session beans in the conversatoin context. Alright so what about this Seam conversation stuff? Lets work one up. First we need a bean that would be the conversation context. So we use our counter code and add
@Scope(ScopeType.CONVERSATION)

for
package org.jlowrey.examples.sessionbeans;
 
import org.jboss.seam.annotations.Name;
import javax.ejb.Stateless;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Scope;
 
@Name("StatefulConversation")
@Scope(ScopeType.CONVERSATION)//note that this is just a Java Bean (no @Stateful or @Stateless to turn it into an EJB so it needs the @Scope(ScopeType.CONVERSATION) else it would be in the Event context.
public class StatefulConversationBean
{
    private int count = 0;
    public int getCount()
    {
	    count++;
	    return count;
    }
 
    @Begin
    public void startConversation()
    {
    }
 
    @End 
    public void endConversation()
    {
    }
 
}
Now, how do we "start" this conversation? For example what happens if we just call the bean over and over again with no other changes? The docs say that the default conversation is only for the length of the request. Huh? The amount of time that state is held for a "session bean" in the "conversation context" is only for the length of the request. So just calling getCount will always return 1. Ok, but if we call startConversation() everything changes. We'll do that with an s:link. We'll also add more s:link
s to continue and end the conversation. Here's the page,
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:s="http://jboss.com/products/seam/taglib" >
<br/>
Stateless has been called: <b>#{Stateless.getCount()}</b> times.<br/>
StatelessEJB has been called: <b>#{StatelessEJB.getCount()}</b> times.<br/>
Stateful Session has been called: <b>#{Stateful.getCount()}</b> times.<br/>
Stateful Conversation has been called: <b>#{StatefulConversation.getCount()}</b> times.<br/>
<b><s:link value="Start a conversation" action="#{StatefulConversation.startConversation()}"  /></b><br/>
<b><s:link value="Stay in a conversation" view="/test.xhtml"  /></b><br/>
<b><s:link value="End conversation" action="#{StatefulConversation.endConversation()}"  /></b><br/>
</html>

looking closely at the status bar we see a query param "cid=6". We picked this up after click the start conversation link. As long as we click "stay" it continue to be "cid=6". We can reset the conversation counter by clicking the "end" link.

Comments

This was great!

My only question is that with the StatelessEJB, you mention that it holds state with an improper use of a Stateless EJB. What would be the proper use? I've been searching all over for this.

TIA,
Mike

jimlowrey's picture

Proper use of StatelessEJB

Anything that does not rely on instance variables in the statelessEJB. Anything that is basically atomic to that request. For example suppose you want to show some statistic from your db that must be recomputed every time a request is made.

public int getStat()
{
  //don't appeal to any instance variables for this class
  //'cause this class may have been recycled.

  //do a db query and return some bit of info like how many comments to a post.
  //select count(*) from comments where comment_id=34
  return commentCount;
}

Or let a user submit some data that you will store. As long as you don't appeal to any information that the class in question could store in an instance variable.

For each request the container (JBoss) is allowed to reuse an already instantiated class or it can create a new one with fresh instance variables. Consider that you know what you are doing to a given instance variable for a given class. However since you don't know if the class has been used already, you don't know if said "doing" has been done. You could write stuff in to keep track, but that is what stateful beans are for.

Thank you!!!

Thank you for clarifying this for me! We are having some MAJOR conversation/state issues in our application, and your help and this post is much appreciated!

Thanks again!
Mike

Good Job!

Thank for explaining this as it is one of the tricky topics when you start using Seam.

jimlowrey's picture

Thanks

Glad to see someone found this :-)

@Stateless

Dude, what happened with the @Stateful annotation. Since you are talking about a @Stateful EJB you do not show any of a kind.

These are just POJOs in different scopes (except the first one) rather than EJB's.
I think you should fix either the text or the code ;)

Cheers.

jimlowrey's picture

No @Stateful EJBs here.

Actually I am not talking about a @Stateful EJB. Just a bunch of beans.

Perhaps the issue is the use of the term 'state'. By state I mean state being held between requests by the server on behalf of the user. Nothing to do with EJBs. EJBs may appeal appeal to this definition of state, but this definition of state knows nothing of EJBs. It is the classic definition of HTTP session state as I understand it.

These are just examples of "conversations" with various beans (the pun is intended). Specific care is taken to illustrate the life of these beans as they are instantiated, used, and possibly reused by the container depending on what annotation is used.

@Stateful wasn't an example because, for me, it wasn't confusing and didn't have any interesting side effects. @Stateless beans on the other hand were interesting.

Does that help?