Skip to main content

Stateful Session Bean [One instance per client ? Really ?]

5 replies [Last post]
goundy
Offline
Joined: 2008-11-09

Hi everyone.
I wrote a simple program to practice managing sessions using Stateful EJB.
I read: Each connected client gets an instance of the Stateful session been that is called.
But in my test, each time I refresh the page (simple F5) I get newest instance (tested with Object.hashcode())... I really don't understand how come

Here's some of my code:
My SessionBean Implementation:

package helb.test.ejb;</p>
<p>import java.util.ArrayList;<br />
import javax.annotation.PostConstruct;<br />
import javax.ejb.Stateful;</p>
<p>@Stateful(name="SessionManager", mappedName="ejb/SessionManagerJNDI")<br />
public class SessionManagerImpl implements SessionManager, SessionManagerLocal {</p>
<p>    private ArrayList users;</p>
<p>    @PostConstruct<br />
    public void Initialize (){<br />
        this.users = new ArrayList();<br />
        Integer i = this.hashCode();<br />
        this.users.add("Hash: " + i.toString());<br />
    }</p>
<p>    public void addUser (String user){<br />
        this.users.add(user);<br />
    }</p>
<p>    public void delUser (String user){<br />
        this.users.remove(user);<br />
    }</p>
<p>    public String findUser (String user){<br />
    	String res = "";<br />
        for (String s:this.users)<br />
        	if (s.equals(user))<br />
        		res = user;<br />
        return res;<br />
    }</p>
<p>    public ArrayList listUsers (){<br />
    	return (this.users);<br />
    }</p>
<p>}

My JSP client page:

<br />
<...><br />
<%@page import="helb.test.ejb.*,java.util.ArrayList"%><br />
<%@page import="javax.naming.InitialContext,javax.naming.NamingException" %></p>
<p><%<br />
InitialContext ictx;<br />
SessionManager ism;</p>
<p>	out.println("Start..." + "");</p>
<p>try {<br />
		ictx = new InitialContext();<br />
		ism = (SessionManager) ictx.lookup("ejb/SessionManagerJNDI");</p>
<p>	     ism.addUser("Entry 1");<br />
	     ism.addUser("Entry 2");</p>
<p>     ArrayList v = ism.listUsers();</p>
<p>     for (String user:v){<br />
         out.println("- " + user + "");<br />
     }</p>
<p>     out.println("Finished listing users" + "");<br />
     out.flush();</p>
<p>	} catch (NamingException e) {</p>
<p>		StackTraceElement[] ste = e.getStackTrace();<br />
		for (StackTraceElement S:ste)<br />
			out.println(S.toString() + "");<br />
	}<br />
%><br />
<...>

Note: In my code I'm talking about "users" but let's say these users are just simple entries related to the connected client (like items in a shopping cart)

Am I missing something ?

Thanks :)

Message was edited by: goundy

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
gaston500
Offline
Joined: 2008-11-24

I've got the same question. So... If I get:
InitialContext context = new InitialContext();
SessionRemote ejbSession = (SessionRemote) context.lookup("Bean30");

How can I get the same ejbSession object ?.

sorincalex
Offline
Joined: 2008-11-22

It's up to the client to access the same session object.
In order to get the same one, you should cache your ejbSession in some data structure, so when you need it next time you get it from there (see method getService below):

public class ServiceLocator {
/**
* Singleton Instance of this class
*/
private static ServiceLocator serviceLocator = null;

// just for the test
private static int counter = 0;

/**
* InitialContext object
*/
InitialContext context = null;

/**
* Cache where the objects can be stored for later
* retrieval.
* This enhances the performance.
*/
HashMap serviceCache = null;

/**
* Constructor to initialize the class
*
* @exception NamingException In case an exception is
* generated
*/
public ServiceLocator() throws NamingException {
// Start the initial context
context = new InitialContext();
// Initialize the HashMap to store 5 objects
serviceCache = new HashMap(5);
}

/**
* Returns the singleton instance
*
* @exception NamingException In case an exception is
* generated
* @return Singleton Instance
*/
public synchronized static ServiceLocator getInstance() throws NamingException {
if (serviceLocator == null) {
// If the object is not created, then create it
serviceLocator = new ServiceLocator();
}

// Return the singleton instance
return serviceLocator;
}

/**
* This is the method that returns the service
*
* @param jndiName JNDI Lookup needed for invoking the
* service
* @exception NamingException In case an exception is
* generated
* @return Service Object
*/
public Object getService(String jndiName) throws NamingException {
if (!serviceCache.containsKey(jndiName)) {
// If the object is not saved in the cache, then do a
//lookup and save it
serviceCache.put(jndiName, context.lookup(jndiName));
}
// Return the required object
return serviceCache.get(jndiName);
}

}

ksak
Offline
Joined: 2005-05-20

The reason is that each lookup() of an EJB 3.0 stateful session bean Remote or Local business interface results in the creation of a new bean identity. Each reference returned from
the lookup refers to a different stateful session bean. It's up to the caller to determine how it
wants to manage access to that reference. Typically a web application will store the reference
in an HttpSession or application-wide (ServletContext) scope for subsequent access.

goundy
Offline
Joined: 2008-11-09

Thank you very much.
I thought the session bean storage was managed by the ejb container, but now I get the logic of how it works.

Man, really thank you very much ;)

sorincalex
Offline
Joined: 2008-11-22

Indeed, the bean identities are different when doing several lookups.
The ServiceLocator pattern would help here. In short, you'd hash the result of the lookup() so that when you need it next time, you get it from the hash, so you preserve the bean identity.

Don't forget in your case we're dealing with 2 types of sessions: the bean session and the web session. The former ensures that once you request a stateful bean, it's identity remains the same across that user session. But when you use the latter, you have a web session on top of the bean session. To ensure that you access the same bean from 2 different JSPs (or when you do a reload), you need to store the bean's identity into the web session scope.
Coming back to the ServiceLocator pattern, you'd store the ServiceLocator instance into the web session scope so that any time you require your session bean from any JSP page (or from a reload) you'd get the same identity.