Larry,
Great example!
There are two other aspects by which we can complicate this picture further:
1) the session bean needs to access/modify the session context initially created by
the servlet
(modelled here by HTTPSession)
2) introduce the notion of process-affinity amongst the set of servlets (or JSPs) and
session beans that
belong to a session.
The latter case above has implications on fault isolation and failure recovery, issues
that are not so
easy to deal with in an EJB/servlet runtime that is distributed.
I am sure we need to do some more work on formalizing the notion of a session (or
session state) that
involves different kinds of components like servlets and EJBs.
My $0.02.
Regards,
Sanjeev K.
LAURENCE CABLE wrote:
> > 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
n:Kumar;Sanjeev
tel;cell:(650) 740-8501
tel;fax:(650) 654-6208
tel;home:(510) 226-7011
tel;work:(650) 506-4142
x-mozilla-html:FALSE
url:http://www.oracle.com
org:Oracle Corp.;Java & Object Services, ASG, Server Tech
adr:;;500 Oracle Parkway, Box 659604;Redwood Shores;CA;94065;USA
version:2.1
email;internet:[EMAIL PROTECTED]
title:Development Manager
end:vcard