> My question is how do I maintain session state when using HTML, Servlets and
> EJB's?
>
> Scenario:
> 1) An HTML client(view) invokes a servlet. 2) The servlet(controller) calls an
> EJB Session bean (Model). 3)The Servlet sends a response back to the client and
> terminates. The HTML client invokes the servlet to do something
> else, and so on.
>
> Question:
> 1) How do you maintain a reference to the Session Bean once the Servlet
> destroy() method is called?
>
Hi Mike,
Good question, let me try to give you some guidance!
This is actually a very interesting problem to solve due the
interaction
of Servlet lifecycle and EJB semantics, and one that we are working on
for the Java2 Platform Enterprise Edition Application Programming
Model.
Let's start by restating your problem; how does one persist
SesssionBean
reference(s) across Servlet instance lifecycles?
Although you do not state it I presume that we are dealing with a
STATEFUL
SessionBean, since if it were STATELESS I would probably recommend that
(going slightly off topic here) you simply obtain and cache the
appropriate
EJBHome interface to the SessionBean class as follows:
- in a static class variable in the Servlet class
- lives for as long as the Servlet Class is loaded
(this is implementation dependent)
- per Servlet Class
- in an instance variable in the Servlet
- lives for as long as the Servlet instance lifecycle
(init() -> destroy())
- at least one per instance
- in the ServletContext using {set|get}Attribute()
- lives as long as the ServletContext
(this is also implementation dependent)
- at least one per ServletContext
- in the HttpSession using {put|get}Value()
- lives as long as the HttpSession
(until it is invalidated or the session times out)
- at least one per HttpSession
and re-create an instance each time Servlet.service() is called,
although
one could argue which is more efficient, to re-create, or to hold onto
the
object reference itself, I believe the appropriate usage is to
re-create
since by definition (in the spec) a STATELESS SesssionBean has no
conversational
state.
(back on topic now)
Given that we have a STATEFUL SessionBean we cannot/do not want to
re-create it
for every invocation of Servlet.service(). Furthermore, since both a
particular
Servlet instance lifecycle may be shorter than that of the HttpSession,
and that
the HttpSession itself may expire, we need to be able to preserve the
reference
across both Servlet and HttpSession lifecycles.
In order to preserve the SessionBean across a Servlet instance's
lifecycle I recommend
that you try the following (which is of course greatly simplified for
clarity):
class ExampleServlet implements HttpServlet {
// ...
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ... {
HttpSession session = req.getSession(true);
ShoppingCart scart = null;
if (session.isNew()) { // it's a new session create the
session bean
...
ShoppingCartHome scartHome = ...; // lookup/obtain the
Remote
EJBHome via JNDI
scart = scartHome.create(...); // create instance of
shopping
cart ...
session.putValue("ShoppingCart", scart); // save it in the
HttpSession
} else {
scart = (ShoppingCart)session.getValue("ShoppingCart");
}
// use the shopping cart ...
}
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ... {
// same strategy as doPost() ...
}
// ...
}
Now there is one problem with this ... what happens when the
HttpSession times out, or
in otherwise invalidated ... well then we need to preserve the instance
elsewhere.
One approach would be to save it as a client cookie ... as follows:
class BetterExampleServlet implements HttpServlet {
// ...
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ... {
HttpSession session = req.getSession(true);
ShoppingCart scart = null;
if (session.isNew()) { // it's a new session create the
session bean
...
// first we look to see if the client already has a
Serialized
Handle to the
// SessionBean stroed as a cookie?
Cookie cookies = req.getCookies();
Cookie beanCookie = null;
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName() == "EJBHandleCookie") {
beanCookie = cookies[i];
break;
}
if (beanCookie != null) { // previously encoded ...
String encodedHandle = beanCookie.getValue();
// obtain the encoded serialized handle to the
SessionBean
// from the previously created client cookie.
// use a mythical BASE64 decoder to decode the encoded
value into
// a byte array ....
byte[] buf =
ABase64Decoder.decodeAsByteArray(encodedHandle);
// ignoring the appropriate try blocks for clarity ...
ByteArrayInputStream bais = new
ByteArrayInputStream(buf);
ObjectInputStream ois = new
ObjectInputStream(bais);
scart = ((Handle)ois.readObject()).getEJBObject(); //
may fail ...
// if the object referenced by the handle does'nt
exist anymore then
// we'll have to re-create it ... this is problematic
since we have
// probably lost state due to an EJB server failure ...
}
if (scart == null) { // no existing object ...
ShoppingCartHome scartHome = ... ; // lookup/obtain the
Remote EJBHome
scart = scartHome.create(...); // create instance of
shopping
cart ...
// now save the bean as a cookie ...
// ignoring the appropriate try blocks for clarity ...
ByteArrayOutputStream baos = newByteArrayOuputStream();
ObjectOutputStream oos = new
OutputOutputStream(baos);
oos.writeObject((Serializable)scart.getHandle());
oos.close();
baos.close();
// use a mythical BASE64 encoder to encode the
serialized handle as
// a string ...
String encodedHandle =
ABase64Encoder.encodeAsString(baos.toByteArray());
resp.addCookie(new
Coookie("EJBHandleCookie",encodedHandle));
}
session.putValue("ShoppingCart", scart); // save it in the
HttpSession
} else {
scart = (ShoppingCart)session.getValue("ShoppingCart");
}
// use the shopping cart ...
}
}
I've done a little hand waving here for the sake of brevity but I
believe that the idea
is sound, I hope this helps!
- Larry Cable,
Senior Staff Engineer,
Java Enterprise Technologies,
Sun Microsystems Inc.
begin: vcard
fn: Laurence Cable
n: Cable;Laurence
org: Java Software
adr: Sun Microsystems Inc.,;;(MS: UCUP02-201), 901 San Antonio Road,;Palo Alto;CA;94041;USA
email;internet: [EMAIL PROTECTED]
title: Senior Staff Engineer
tel;work: 408-343-1776
tel;fax: 408-863-3195
x-mozilla-cpt: ;0
x-mozilla-html: TRUE
end: vcard