It's now Saturday, 2005.May.21, at 10:14. Earlier today I download a simple HTML form for login, then cleaned out all the cruft to yield the simplest possible login form. Later I'll be generating a structured representation of such a form, and converting from the structured representation to linear HTML text. But first, I need to finish work on the multi-UI adapter for servlets, specifically wrapRequest (mostly done) and wrapResponse (to do now), later also unwrapResponse. First I'm cleaning up the stuff from last night, and making a full unit-test rig in HSA.main() including one case not yet tested, fixed one small design bug, done, working perfectly, just the way I want it to for non-HTTP cases at 11:59, now back to jsp to verify it still works for HTTP case ... it cached the old code, so I'll have to stop server at 12:00 ... down at 12:03, restarting ... back up at 12:06, started browser screen refresh which requires recompiling JSP, but I forgot to copy the changed class files from my debugging directory to system directory, but I had forgotten to change the text of the trace print in the HTTP cases, so I have no way to know whether old or new code is being run. Fixed that trace text, recompiled, ran unit tests again, remembered to copy class files to system area this time, refreshed again, still getting old code, need to shutdown J2EE server ... down at 12:21, restarting ... up at 12:26, refreshing ... server error: A Servlet Exception Has Occurred Exception Report: javax.servlet.ServletException: MyToken Root Cause: java.lang.NoClassDefFoundError: MyToken at Sexpr1.toSexpr(Sexpr1.java:113) This should not be a problem. It's just that I am using one of my classes that I hadn't been using before, so I need to (1) copy the class file to the system area ... done, (2) update the jsp to declare usage of that class ... done, (3) reload WebPage which forces recompilation of jsp, ... done but still getting the error. This should have been enough to assure that it's known to the jsp environment: <%@ page import="MyToken" %> At 12:39 I'm stopping the server ... done at 12:42, restarting ... back up at 12:46, reloading Web page ... still getting error, what if I also force that class to be used as a bean? Trying that ... 12:50 reloading WebPage ... new error: A Servlet Exception Has Occurred Exception Report: javax.servlet.ServletException: Cannot create bean of class MyToken at org.apache.jsp._0002fDataFlow_jsp._jspService(_0002fDataFlow_jsp.java:92) Root Cause: java.lang.ClassNotFoundException: class MyToken : java.lang.InstantiationException: MyToken at java.beans.Beans.instantiate(Beans.java:211) That last error gives me a clue, checking ... yup, this class was never intended to be used as a bean, and it's meaningless to have a zero-arg constructor. So I'll have to make up a stupid no-arg default case just to satisfy JavaBeans ... OK, I made up an appropriate no-arg default constructor, next to move files from src/classes directories to working/test directory then compile and unit-test ... done at 13:08, shutting down server ... back up at 13:16, reloading Web page ... now it loads the file and instantiates it just fine as a bean, but then gives the original error: A Servlet Exception Has Occurred Exception Report: javax.servlet.ServletException: MyList at org.apache.jasper.runtime.PageContextImpl.handlePageException (PageContextImpl.java:452) Root Cause: java.lang.NoClassDefFoundError: MyList at Sexpr1.toSexpr(Sexpr1.java:114) at Sexpr1.toSexpr(Sexpr1.java:102) at HSA.wrapRequest(HSA.java:54) I give up trying to make this work. I'll change the debug info here to just use the cruddy Java Object printer instead of my nice s-expression printer at this point in HSA, done, but it's still cached in server, shutting down server, back up at 13:35, reload Web page, finally no error! Looking at trace output on J2EE window: ** Start of 13:28 debug of HSA... reqO none of those first cases, reqO=org.apache.catalina.connector.HttpRequestFacade@77896f wrapping and checking further... System runtime determination: reqO instanceof javax.servlet.http.HttpServletRequest, live HTTP session! That looks good. Now looking at WebPage output: * Calling HSA.wrapRequest(reqO) ... ... HSA.wrapRequest(reqO) returned WrappedHttpServletRequest@63cc77 with mode=2 Looking at source to see what mode=2 means: public static final int HTTP = 2; Very good. Indeed the code I finished writing and debugging offline at 11:59, which was one and 3/4 hours ago. So I lost 1 3/4 hours just running a live test on the server. It's really good that I can do almost all my code development offline, right? So back to offline development, of wrapResponse next, after a nap. At 16:53 I'm starting work on wrapResponse. At 18:40 I have the basic framework for wrapResponse done, and tested for null original objects. Next to write code for HTTP Response object, try to get it all done before any testing (because testing via server is so very very slow) ... done editing DataFlow.jsp and HSA.java at 19:03 and already starting server back up ... 19:06 server back up and I have WebPage reloading ... oops, I forgot to copy the latest class files to the system directory, did that now, but of course in the mean time it got an error because MyHttpServletResponse wasn't defined in the old files. Sigh, it's still getting that error even though I fixed the problem, so it seems to have the error cached and I need to shutdown and restart the server again, at 19:14 ... 19:21 it's back up, reloading Web page ... still the same error and I have no idea why: A Servlet Exception Has Occurred org.apache.jasper.JasperException: Unable to compile class for JSP An error occured between lines: 14 and 93 in the jsp file: /DataFlow.jsp Generated servlet error: /usr/java/j2sdkee1.3/repository/adsl-63-192-44-196.roadguy.com/web/date/_0002fD ataFlow_jsp.java:164: Class org.apache.jsp.MyHttpServletResponse not found. MyHttpServletResponse myResp = HSA.wrapResponse(mode, response); I'm going to give up on the server until I think of something else to try to fix the problem. Instead I'll work on the test rig for running servlets without any J2EE server. But first, a nap. It's now 20:41, and towards the end of the nap I came up with an idea how method Sexpr1.toSexpr could have gotten access to class MyToken even though the class wasn't visible there: If you recall from above, the toplevel JSP was able to get a bean for that class, yet even after it had the bean, the low-level class in the same VM couldn't see that class. So what I could have done is have the JSP install the bean as a property in the Request object, and then the low-level class could fetch it via request.getProperty(...) and then perform a Reflection-API invocation. But now I realize that Sexpr1 has no access to the request object, so that wouldn't work. (But perhaps a really major hack would be possible: Include a public static Object variable in class Sexpr1, and the toplevel JSP could copy the bean there, and then the code inside Sexpr1 could use that for an invocation. Not sure if I can figure out exactly how to make it work, and since I could temporarily dumbify my trace output to avoid the problem, it's not urgent to work out a full solution.) But on the other hand, the roadblock I hit just before the nap might be amendable to a bean trick. But on the other hand, it's the very same class, the JSP itself, which is unable to find the class file sitting right under its nose. I'll try anyway at 20:55 ... wait a minute! I think the problem is simply that I failed to do a page import on that class. I'll do that now: <%@ page import="MyHttpServletResponse" %> Reloading Web page ... Yeah, that fixed the problem! No more server error, and the call to HSA.wrapResponse produces correct trace output: ** Start of 17:25 debug of HSA.wrapResponse(2, ...) wrapped-HTTP and Web page comes back with apparently correct return value: * Calling HSA.wrapResponse(2, respO) ... ... HSA.wrapRequest(mode,reqO) returned WrappedHttpServletResponse@633bb8 Next, at 21:10, I need to make sure the session object is accessible from the wrapped Request object just as it was available from the original system-supplied Request object, i.e. via the instance method getSession(). Referring to the printout from my utility that shows all methods of a given class: Name of class: javax.servlet.http.HttpServletRequest 44: public abstract javax.servlet.http.HttpSession javax.servlet.http.HttpServletRequest.getSession(boolean) 45: public abstract javax.servlet.http.HttpSession javax.servlet.http.HttpServletRequest.getSession() Now referring to my notes in LabBook.May17.txt: bookstore1/ BookDetailsServlet.java HttpSession session = request.getSession(true); so that's the version (#44) of the method I'll need to implement. Now the question is: Do I (1) at the time I originally create my own request object, perform a copy of the data from the system request object to my own request object, and then write an accessor for my own request object, so that later it's directly available there? Or do I (2) write an accessor for my own request object that simply calls the accessor for the embedded request object, similar to calling super.methodname in other circumstances? (1) doesn't work because if the parameter is true, it means force creation of a session object if none existed, and my own request-object class has no idea how to do that (it involves complicated maintainance of cookies most likely). But (2) doesn't work either, because in any UI mode except HTTP/J2EE (i.e. in test-rig modes, swing toplevel mode, and CGI toplevel), there is no actual session object whatsoever and no way to make any. I think the solution is to keep a local variable that is any fake session object created in all modes except HTTP/J2EE, and to do the pseudo-super.method call in the HTTP/J2EE case, so the session object is *never* copied and cached in my request object, any session object sitting in my request object must be a fake session object. At 21:28, starting to explore the various methods associated with the various servlet objects (the Servlet itself, its context, the request object, the session object within it, the response object) to get a feel for what they do, prior to starting to implement any of them within my wrapped objects. Then at 23:48 starting to implement the session variable (which is null whenever in HTTP/J2EE mode, and also in other modes only when there is no fake session object yet created) and the getSession(boolean create) method, but it's bedtime already. May 22, I started the day by doing experiments with System.currentTimeMillis() for purpose of generating a random fake session ID when not running under a J2EE/Servlet container which supplies a real session ID. In the process I needed to practice working with TreeMap, and I needed to use MyInput so I finally got around to fixing it to convert all checked exceptions to unchecked exceptions so you don't have to wrap every individual call in a try...catch combination. Also I wrote a complete test rig for MyInput as a separate class. But I cut this side excursion short to get back to the main project. Wrote FakeHttpSession, which at instantiation time uses a synchonized method to guarantee that no other thread on the same JVM will get exactly the same millisecond time which is used as the main part of the fake session ID. Sample results before that extra care was installed: Session ID#1 = fake1116824134815ms Session ID#2 = fake1116824134820ms Session ID#3 = fake1116824134820ms and after it was installed: Session ID#1 = fake1116825472532ms Session ID#2 = fake1116825472540ms Session ID#3 = fake1116825472542ms (Yes, those are actual System.milliseconds values, so if you really want to know the exact time I ran those tests you know how to find out.) Upgraded my runtime check of classes satisfying interfaces to include both javax.servlet.http.HttpSession and FakeHttpSession satisfying (implementing) MinimalHttpSession. Decided that the constructors for WrappedHttpServlet[Request/Response] should take a parameter which is the system Request/Response object being wrapped, or null pointer if the wrapper is virgin (not wrapping anything, acting stand-alone). Changed both constructors, and their calls from HSA, accordingly. It's now 16:57. Next I need to make the wrapped Request object have a link to a session object, in *all* cases live-HTTP and fake/debug. The interface spec for javax.servlet.http.HttpServletRequest doesn't include any setSession, but it does have getSession(boolean) which forces a new session to be set up if none existed, so I guess in the case of fake/test rigs I can use that, and have the call to new FakeSession from within the getSession method of the request object if it's a virgin (not wrapping anything) request object. At 17:45 I'm completely stumped over a compiler diagnostic: WrappedHttpServletRequest.java:38: cannot resolve symbol symbol : variable mySession location: class WrappedHttpServletRequest this.mySession = new FakeHttpSession(); ^ mySession is clearly defined just a few lines above, so why the problem?? I tried changing the declaration to say Object instead of the correct type, but that didn't help. I tried moving the declaration from where it was to immediately before the method where I was trying to use it, after the introductory comment. Now suddenly it is working, with error about incompatible type when I try to return it, which means it's seeing it now. Aha! Just before the place the declaraction was originally, there was the following: private Object wrappee = null; /* System request object being wrapped by this, which is set by the constructor. If null after constructor returns, this is a standalone request object, i.e. not wrapping around anything. * // The following fields are effective only if wrappee is null: (here's where the declaraction was being ignored) Do you see what the problem was? Moving ahead at 18:00 ... OK, I think I've implemented that stuff, wait a minute, I was so burned out over that stressful compiler diagnostic that I almost forgot to update the test rig, done now: WrappedHttpServletRequest wr = new WrappedHttpServletRequest(null); System.out.println("wr.getSession(false) = " + wr.getSession(false)); System.out.println("wr.getSession(true) = " + wr.getSession(true)); System.out.println("wr.getSession(false) = " + wr.getSession(false)); System.out.println("wr.getSession(true) = " + wr.getSession(true)); System.out.println("Session ID: " + wr.getSession(true).getId()); and here's the output from that code: wr.getSession(false) = null wr.getSession(true) = FakeHttpSession@4b222f wr.getSession(false) = FakeHttpSession@4b222f wr.getSession(true) = FakeHttpSession@3169f8 Session ID: fake1116836885460ms Hey, it's a good thing I put in a full unit-test rig there, because there's a bug, do you see it? So I fixed the bug, a line of code missing, and re-ran the test rig. Here's the new output: wr.getSession(false) = null wr.getSession(true) = FakeHttpSession@4b222f wr.getSession(false) = FakeHttpSession@4b222f wr.getSession(true) = FakeHttpSession@4b222f Session ID: fake1116837274810ms It's working correctly now. Moving on to four methods that the response object needs implemented: response.setContentType("text/html"); // Finished/tested at 19:06 response.setBufferSize(8192); // Finished/tested at 19:19 response.resetBuffer(); // Finished for HTTP case at 19:23, fake late but I'm stuck on this one: PrintWriter out = response.getWriter(); I wanted to simply feed all that output to System.out in the faked testrig version of wrapped HTTP response object, but PrintWriter is an incompatible data type with PrintStream. I tried passing System.out as parameter to constructor for PrintWriter, and that was accepted by the compiler, but none of the output sent to the PrintWriter ever appears on standard output. It's now 20:03, so this one failed task has already burned 40 minutes of my time. Time for a nap. OK, I've decided how to resolve this. I was planning eventually anyway to implement a way for the innerds of a servlet to write not just text but form objects to the output stream, where form objects get converted to linearized HTML text in the case of a live HTTP session, but get buffered up and entered as elements in a vector in the case of a swing or test/debug session. Well, if I do that right now instead of later then I won't have to figure out how to make System.out behave like a PrintWriter. So from inside the servlet, the return value of response.getWriter() will be an object of type ClientWriter which will not be a sub-type of PrintWriter. Starting to do that now at 20:29. ClientWriter written and tested, now 22:11 and ready to incorporate that into the wrapped response object ... OK, it's in there, and tested. Typical code to call it from the test rig looks like this: wr.getWriter().println("** This text via Wra...Resp writer"); System.out.println(wr.getWriter().toSexpr()); wr.getWriter().printXml(); The first line prints some text as if it were a regular output stream, the other two lines reveal the contents so-far in s-expression form and XML form to prove the data is really there. It would be the job of the response-object unwrapper to do one of those (working) or render into HTML (not yet written). It's now 22:40. ClientWriter out = response.getWriter(); // Working = request.getContextPath(); // Written and tested, 23:09 response.encodeURL(request.getContextPath() + "/catalog") + At 23:16 I've looked encodeURL online and decided I need some online experiments to see exactly how the system version encodes URLs when cookies aren't accepted, so that my code can do the same when testing offline. Meanwhile, I've put the wrapper in, whereby if online it just passes through the call, and otherwise it throws an exception to remind me to do it. That completes the checklist of methods commonly called by the sample servlets. Tomorrow I can start to write the actual test rig. Now Monday at 06:07. First I'll enhance the scriptlet in DataFlow.jsp to include the URL encoding I was wondering about ... hmm, getting strange error: A Servlet Exception Has Occurred Exception Report: javax.servlet.ServletException: WrappedHttpServletRequest: method ()V not found at org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:452) Root Cause: java.lang.NoSuchMethodError: WrappedHttpServletRequest: method ()V not found at HSA.wrapRequest(HSA.java:84) I've looked at that line of code in source: MyHttpServletRequest wReq = new WrappedHttpServletRequest(reqO); There's some trace output just before that line, which confirms that's where the program got to before it died. Hmmm, that file hasn't been compiled since it was previously edited, and attempt to compile now fails, gotta work on that first ... hmm, one compile error was just a variable with wrong capitalization, apparently something I typed in and forgot to compile, other was calling a function which has since been removed from the interface, maybe that confused the runtime exception location reporting mechanism when it looked in that obsolete class file to find what was being called? Anyway fixed the capitaization typo and commented out the line in the test rig that called the now-non-existant interface function, and now it compiles successfully. Now at 07:33, going to copy that compiled class file to system area ... done, started re-load of Web page ... done but still getting the same error at the same line number despite the fact I added another trace statement just before it so the problem should be one line later in the file. Shutting down J2EE server to flush cache, now 07:39 ... restarted it ... at 07:49 it's back up and I'm re-loading the Web page ... ah, much better, now it's getting one of my own exceptions where I have a stub for code I need to write. Now 08:00 and I can get working on it. It's a stub I have for setAttribute, fixing it ... all done, reloading Web page at 08:18 ... nop, it's in the cache, shutting down server at 08:23 ... restarting it ... 08:34 it's back up, reloading Web page ... ClassCastException because I mistakenly tried to cast it to my new minimal interface which in fact the object implements, but Java compiler insists all casts are in stone so-to-say at the time the class was compiled, fixed in source and stopping server already, done, restarting server and fixing other similar places in code and compiling source etc., trouble at 08:53: Return type of getSession must be compatible with both the system supplied session of type javax.servlet.http.HttpSession and my own fake session of type MyHttpSession, both of which satisfy the interface MinimalHttpSession, which is currently the return type of WrappedHttpServletRequest.getSession, but the compiler won't accept that. So do I need to declare my method of return type Object and then servlets can't ever call methods of it? I did a grep of all the sample servlets: Each one does one of these: HttpSession session = request.getSession(); HttpSession session = request.getSession(true); I checked online. javax.servlet.http.HttpSession is just an interface, not a class, so I can implement it simply by supplying stubs for everything I'm not defining, and then I can declare that FakeHttpSession implements HttpSession and both the servlets and my adapter code should compile correctly. That's a lot of stubs to write, but not a horribly large number, and I can use my runtime interface/class checker to give me a list of those methods not already implemented. Trying that now at 09:08 ... at 09:26 I finally got the file to compile so that it'd be possible for the interface checking utility to see the current class file for it. Ran checker, found 9 methods of the interface that I haven't yet implemented, not a huge task, especially since I can copy&paste from the interface-violation report, doing that now ... edit done at 9:38, compiling ... one error, fixing ... trouble with deprecated method still specified in the interface, but finally able to compile and check at 10:01. Yes, it satisifes the interface. So now I can finally put the "implments" clause in the source to make the compiler happy when I try to bind such objects to variables of that interface class ... so now it gives not one but *six* warnings about deprecated methods that I implemented *only* to satisfy the interface requirements!! FakeHttpSession.java:89: warning: javax.servlet.http.HttpSessionContext in javax.servlet.http has been deprecated public javax.servlet.http.HttpSessionContext getSessionContext() { ^ FakeHttpSession.java:6: warning: removeValue(java.lang.String) in javax.servlet.http.HttpSession has been deprecated public class FakeHttpSession implements javax.servlet.http.HttpSession { ^ FakeHttpSession.java:6: warning: putValue(java.lang.String,java.lang.Object) in javax.servlet.http.HttpSession has been deprecated public class FakeHttpSession implements javax.servlet.http.HttpSession { ^ FakeHttpSession.java:6: warning: getValueNames() in javax.servlet.http.HttpSession has been deprecated public class FakeHttpSession implements javax.servlet.http.HttpSession { ^ FakeHttpSession.java:6: warning: getValue(java.lang.String) in javax.servlet.http.HttpSession has been deprecated public class FakeHttpSession implements javax.servlet.http.HttpSession { ^ FakeHttpSession.java:6: warning: getSessionContext() in javax.servlet.http.HttpSession has been deprecated public class FakeHttpSession implements javax.servlet.http.HttpSession { ^ 6 warnings I'll leave it in that sorry state because I'm too tired to fool around with it any more. Back to the file I was actually working on... done and compiled and both files copied to system area, and server already restarted an hour ago, and at 10:21 I'm starting re-load of Web page ... done: java.lang.ClassCastException: org.apache.catalina.connector.HttpRequestFacade at WrappedHttpServletRequest.setAttribute(WrappedHttpServletRequest.java:67) at HSA.wrapRequest(HSA.java:92) OK, I think I fixed the problem, recompiled, moved files, shut down server, restarting server at 10:39 ... up at 10:44 and reloading Web page ... 10:45 done: A Servlet Exception Has Occurred Exception Report: javax.servlet.ServletException at org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:452) at org.apache.jsp._0002fDataFlow_jsp._jspService(_0002fDataFlow_jsp.java:299) Root Cause: java.lang.IncompatibleClassChangeError at HSA.wrapRequest(HSA.java:92) at HSA.wrapRequest(HSA.java:32) at org.apache.jsp._0002fDataFlow_jsp._jspService(_0002fDataFlow_jsp.java:173) So in fixing that previous problem, I seem to have broken something earlier. Here's the offending line of code: wReq.setAttribute("org.maasinfo.mode", new Integer(HTTP)); and here's the declaration of the variable: MyHttpServletRequest wReq = new WrappedHttpServletRequest(reqO); aha, here is the problem: I castrated the declaration of that class when I was hassling over compile-time failures and changing the specification of the interface, and I forgot to change it back: public class WrappedHttpServletRequest { //implements MyHttpServletRequest grep shows that's the only file currently castrated like that, so I have just that one to fix. Stopping server first, so we can overlap ... OK, running shutdown sequence, anyway as I was saying, even if a class does in fact implement an interface, as mine does, the compiler won't allow such an assignment unless the class *declares* that it implements the interface. Anyway, server down and now being restarted, at 11:04 I'm waiting for it ... done at 11:07, waiting for Web-page re-load ... done at 11:11, crappity crap!! With all the hassle over those problems, I forgot to fix the source like I said before recompiling and copying and shutting down and restarting server and reloading Web page, so that whole cycle was totally wasted. Doing it all over again at 11:13 ... compiler problem: WrappedHttpServletRequest.java:11: getSession() in WrappedHttpServletRequest cannot implement getSession() in MinimalHttpServletRequest; attempting to use incompatible return type found : javax.servlet.http.HttpSession required: MinimalHttpSession public class WrappedHttpServletRequest implements MyHttpServletRequest { ^ At 11:38 I'm done editing compiling and copying, waiting for server to finish restarting ... at 11:43 it's done, now waiting for Web-page re-load to finish ... done at 11:44, with new error: A Servlet Exception Has Occurred java.util.MissingResourceException: STUB: Need to look at this code in WrappedHttpServletRequest at WrappedHttpServletRequest.stub(WrappedHttpServletRequest.java:60) at WrappedHttpServletRequest.getAttribute(WrappedHttpServletRequest.java:75) at org.apache.jsp._0002fDataFlow_jsp._jspService(_0002fDataFlow_jsp.java:177) OK, let me change that like I fixed another, and change as many others as I can in the same batch, hopefully without breaking anything ... hmm, that was the last full-stub in the file. Here's the new code: public Object getAttribute(String key) { if (wrappee != null) { if (wrappee instanceof javax.servlet.http.HttpServletRequest) { javax.servlet.http.HttpServletRequest req = (javax.servlet.http.HttpServletRequest)wrappee; return req.getAttribute(key); } throw new java.util.MissingResourceException ("Bug: Wrappee non-null but not of correct type", "",""); } stub(); return null; //props.get(key); } I'm done at 12:03, just waiting for server to finish restarting ... done 12:07 reloading Web page ... 12:11 error: A Servlet Exception Has Occurred java.util.MissingResourceException: STUB: For resetBuffer, did wrap but need fake version now at WrappedHttpServletResponse.resetBuffer(WrappedHttpServletResponse.java:81) at org.apache.jsp._0002fDataFlow_jsp._jspService(_0002fDataFlow_jsp.java:205) That's weird. The message says it's running non-wrap (local-test) mode which needs fixing after wrap already fixed. But this is wrap around HTTP mode. Aha, the return statement from the wrap case was missing causing it to fall into the non-wrap code. Here it is fixed now: public void resetBuffer() { if (wrappee != null) { ((javax.servlet.http.HttpServletResponse)wrappee) .resetBuffer(); return; } throw new java.util.MissingResourceException ("STUB: For resetBuffer, did wrap but need fake version now", "",""); } edited, compiled, copied, at 12:22 I'm just waiting for server to finish restarting ... done at 12:27, now waiting for Web page to reload ... 12:30, hey no exceptions thrown! Now let's see if the output to the Web page, and the system output, are all correct ... looks good. In particular I finally have the info about encodeURL that I wanted to know about for a long time. With cookies: cp = request.getContextPath() = /date cpu = cp/catalog = /date/catalog url1 = response.encodeURL(cpu) = /date/catalog now killing the cookie and reloading Web page and refusing to accept a new cookie: cp = request.getContextPath() = /date cpu = cp/catalog = /date/catalog url1 = response.encodeURL(cpu) = /date/catalog;jsessionid=C91944B0F30431843632D9F007E31177 So that's how the session ID is encoded in URL by the servlet container. By contrast, recall that JSESS is the name of the cookie containing the session ID. (I wonder why the servlet container uses different names for the cookie key vs. the parameter key?) I wonder if session.isNew() is the deciding factor in whether to include the jsessionid parameter or assume the cookie is accepted? I'm going to change the JSP to find out, at 12:45 ... edit done and cookie reset to initial state at 12:48, waiting for Web page to refresh, done at 12:50, it offered me cookie, I accepted, Web page shows: /date/catalog;jsessionid=9647E4D70014F7C083C835B7B5302553 session.isNew() = true cookie jar in browser shows: Domain=localhost (Cookies always allowed.) JSESSIONID=9647E4D70014F7C083C835B7B5302553 Path=/date Hmm, when I got JSESS a few days ago, I was looking at it from the server end, one of the parameters or properties or somesuch, but the actual cookie matches the URL-rewrite except for case (and I notice it uses semicolon, not question mark, which I failed to notice before). Now let me refresh while keeping the cookie ... here it is: url1 = response.encodeURL(cpu) = /date/catalog session.isNew() = false Now let me kill the cookie and reload again ... here it is: url1 = response.encodeURL(cpu) = /date/catalog;jsessionid=5157C1543173B5F7F393B7D1414547C6 session.isNew() = true Notice that the servlet container was *not* able to remember the old session ID, it made up a new one, contrary to what Sukhjit said in class that the server knows the correct session even if all of cookies and URL-rewrites and hidden fields are absent. The truth is that it's the client (browser) which keeps hold of the session ID between HTTP transactions, and tells the server (if one of those three methods are enabled) the old ID during each new HTTP connection, thereby allowing the server to look up the old session in some lookup table to retrieve the old session object. Time to take a nap now at 13:04. After nap, I've looked through notes of the past few days, showing prototype designs of the doGet etc. interface, and looked at recent code showing what I've been testing in live code (both live HTTP/J2EE session and unit tests), and I've now finalized my plan for actual code for doGet interface and adapter/wrapper. Below is copied directly from a comment in DataFlow.jsp: // The live HTTP/J2EE servlet container calls this, and/or likewise doPost: public void doGet(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException { doGetAdapter(request,response); } /* *All* modes of operation pass through here, HTTP/J2EE mode indirectly via the above trivial upcasting interface, most other modes directly from their respective trivial interfaces (similar to above, for example CGI) or fullfledged toplevels (for example swing toplevel) or test/debug rig. Note that a fullfledged quality-control test could be calling this. */ public void doGetAdapter(Object objReq, Object objResp) throws javax.servlet.ServletException { MyHttpServletRequest wrReq = HSA.wrapRequest(objReq); /* The above uses runtime type-checking to determine whether container was HTTP/J2EE or something else, and sets a "mode" attribute in the returned MyHttpServletRequest object, which is passed to the other wrapper next so that both wrappers agree on the mode in effect: */ Object objMode = myReq.getAttribute("org.maasinfo.mode"); Integer intMode = (Integer)objMode; int mode = intMode.intValue(); MyHttpServletResponse wrResp = HSA.wrapResponse(mode, objResp); try { doGetMain(wrReq,wrResp); } catch (Throwable ex) { throw new java.util.MissingResourceException("msg..." + ex, "",""); } finally { MySerletAdapter.unwrapResponse(mode, wrResp); /* modifies embedded objResp object to convert output to correct format (HTML or XML or s-expr or JFrame etc.) depending on the mode, and to put that converted output in the place where the container expects to find it. */ } public void doGetMain(MyHttpServletRequest wrReq, MyHttpServletResponse wrResp) throws javax.servlet.ServletException { wResp.setContentType(resp, "text/html"); wResp.setBufferSize(resp, 8192); /* The rest of the innerds of the servlet will be discussed later. */ } Now it's 15:25 and next I'll convert that to strictly inline/linear jsp-scriptlet code, to verify that it still works correctly after I've cleaned it up, and to test the unwrapper which I haven't yet written, and to lead into an inline/linear form of a sample servlet ... done converting at 16:18, now to try it for the first time in this form ... at 16:23 lots of compiler diagnostics on code that looks perfectly fine, probably mismatched quotemarks or braces or parens etc. Looking ... aha, I failed to close a scriptlet before saying <%-- to start a JSP comment, so the <%-- and all the contents of the comment were treated as part of the scriptlet, nasty trouble that cause. Fixed it, reloading Web page again ... done at 16:28, looking, two simple typos where I failed to change the name of a variable, you can see that mistake in the proposed actual code I quoted above, where I failed to see the mistake when flattening it. Fixed and reloading Web page again ... hmm, at 16:36 two strange looking errors, fixed them, reloading Web page as I type. I can't imagine how those strange ones slipped in, see sample code again to see them in arg to setContentType and next line. Maybe a trashed edit I didn't notice. Hey, at 16:40, no error, just regular output, looking ... hey, except for a couple minor formatting mistakes in the trace output it seems to be working. I've fixed those format mistakes and am reloading it to present a nice transcript here, done: ** Starting to bind formatting constants... ...done. Object objReq = request org.apache.catalina.connector.HttpRequestFacade@6d1da Object objResp = response org.apache.catalina.connector.HttpResponseFacade@3b625b * Calling HSA.wrapRequest(objReq) ... ... HSA.wrapRequest(objReq) returned WrappedHttpServletRequest@1549af with mode=2 * Calling HSA.wrapResponse(2, objResp) ... ... HSA.wrapResponse(mode,reqO) returned WrappedHttpServletResponse@18a770 Mode 2 is HTTP/J2EE, so it looks correct. Next, after a nap and fixing some food, I'll need to write barebones unwrapper for HTML mode. Resuming work at 22:03. Copying all the code from DataFlow.jsp to the test rig for HSA, except starting point is null req/resp objects instead of live HTTP/J2SE req/resp objects. At 23:05 I have it working through both wrappings. Next I started adding calls to emit text to the output stream of the wrapped response object and emitting it later at unwrap time, but I was too tired to continue past midnight.