Slava Kozlov wrote:
> > -----Original Message-----
> > From: A mailing list about Java Server Pages specification and reference
> > [mailto:[EMAIL PROTECTED]]On Behalf Of Craig R. McClanahan
> > Sent: Sunday, January 16, 2000 11:09 AM
> > To: [EMAIL PROTECTED]
> > Subject: Re: Providing application resources to action classes in
> > single-servlet delegation model
> >
>
> ....
>
> >
> >
> > As you can see, I'm passing a reference to the calling servlet
> > (passed as "this"),
> > along with the specific request and response I am dealing with.
> > (Alternatively, I
> > suppose you could make the servlet a JavaBeans property of the
> > action class, and
> > have the controller servlet set it the first time that the action
> > instance is
> > loaded).
>
> Firstly, I believe the alternative is dangerous - I don't believe the
> Servlet API guarantees the longevity of individual servlet objects. Servlet
> engines can kill off servlet objects and restart them as they see fit
> (including during development). Please correct me if I'm wrong. This would
> mean that the servlet reference that a WebAction was initialized with could
> grow stale and revert to null, crashing the WebAction when it tries to
> dereference it.
>
It's not dangerous when you remember that 'killing off" the servlet means
killing off all the WebAction instances as well. They are simply a set of
processing methods that is split out of what would otherwise be a bunch of
methods in the controller servlet -- accessed by an "if .. else if" chain. The
action instances have no life outside of the servlet that created them. (In
fact, in my implementation they are referenced nowhere but inside the cache
collection of the controller servlet, so they are garbage collected at the same
time that the servlet itself is.)
The code in my controller servlet that actually does this is something like the
following (plus appropriate error checking and exception catching):
// Key = path info selector, value = WebAction instance
Hashtable actions = new Hashtable();
// Key = path info selector, value = WebAction class name
Hashtable mappings = new Hashtable();
public void init() {
...
... Initialize mappings table from init parameters ...
...
}
public void doGet(...) {
// Extract the selector
String pathInfo = request.getPathInfo();
// Retrieve or create a WebAction instance for this request
WebAction action = (WebAction) actions.get(pathInfo);
if (action == null) {
ActionClass actionClass = Class.forName(pathInfo);
action = actionClass.newInstance();
actions.put(pathInfo, action);
}
// Note - single shared instance for any given path info value
// is created the first time it is requested, and then reused
// from then on for the lifetime of the controller servlet.
// Call the perform method for the selected action
action.perform(this, request, response);
}
Inside the web action instance itself, the reference to the servlet lasts as
long as the call does.
>
> > Passing the servlet reference lets me access any resources that
> > the underlying
> > servlet can get.
>
> ...
>
> > If the controller servlet itself had methods you wanted to treat as shared
> > functionality for your action procedures, you could pass a
> > reference to your
> > servlet class instead of HttpServlet if you wanted. I've got a
> > generic controller
> > servlet that does all the common stuff (request parsing, loading
> > and caching the
> > action class instances, calling the correct one), which I can
> > subclass to provide
> > additional shared functionality that is unique to a particular
> > web application.
> >
>
> Judgement call. The more stuff the controller does itself the more Godlike
> it becomes. At some point it may makes sense to decompose it and its
> functionality. Again, judgement call. :-)
>
That's why my generic controller servlet is only 100 lines or so of acual code
-- most of that is initialization, configuration, and error checking. The ten
or so lines showed above are the only meat. It can be used as the controller
for any number of different web apps with zero changes.
For extended functionality, it is up to the designer for a particular web app
-- they can choose to subclass the controller servlet and implement
functionality there, or stick with the generic one and add things in servlet
context attributes (or share them in some other manner). Each app makes that
choice totally independently.
>
> > >
> > > Currently I am just having the controlling servlet extract references to
> > > these beans from the application scope and passing them as
> > parameters to the
> > > action class perform( ) method.
> > >
> >
> > Doesn't that mean that the method signature for perform() changes for each
> > action? That would seem to require changes to the controlling
> > servlet as you add
> > new actions that require different signatures. In my model, the
> > call to perform()
> > is the same for all actions.
> >
>
> I think Drew meant that each perform() method has the same signature, where
> the parameter list includes all the various services.
>
> ...
>
> > There is also a non-zero
> > amount of work
> > allocating a new Java object for the collection, and then filling
> > it in -- and you
> > are paying this cost on every single request.
> >
>
> I think Drew meant that there is but one services Hashtable which gets
> initiated at some point in the beginning and gets passed around to every
> invocation of perform(). Thus no additional per-request object creation.
>
Even if that's the case, then every time you add a new global service you have
to go modify your controller servlet to put one more thing into the hashtable.
You should not have to do something like that.
In the design I suggested, a new service object can be added to the servlet
context attributes, say, and a new (or enhanced) WebAction instance can refer
to it, with zero changes to the controller servlet itself.
>
> > From a design viewpoint, there's an additional question. Let's
> > say you've built a
> > particular web action, and you want to enhance it. But now, it needs an
> > additional resource out of the servlet context attributes that
> > was not required
> > before. Do you need to go back to your controller servlet and
> > add an additional
> > services object to the collection?
>
> Unless you make up a mechanism to add services at runtime (either during
> startup in a config file or dynamically somehow), I don't see how one gets
> around the problem that: for every application-wide service that you add,
> you need to change some core code somewhere (define "core code" as
> non-WebAction). Either by adding a name/value pair to a Hashtable (either a
> self-defined one or ServletContext's) or changing a factory method.
>
The mechanism I use is to put shared stuff in servlet context attributes. As
described above, I can add a new service simply by doing that, and it becomes
available to all web actions -- having to change the controller servlet as well
(to add it to a services collection) is the kind of thing that will be
forgotten and cause bugs.
This approach also works well for shared objects that are added after
initialization of the app -- they become available, instantly, as soon as they
are created. I don't have to go back on every request and make sure I've
copied all of the servlet context attribute objects into my Hashtable.
>
> -s
>
Craig
===========================================================================
To unsubscribe: mailto [EMAIL PROTECTED] with body: "signoff JSP-INTEREST".
FAQs on JSP can be found at:
http://java.sun.com/products/jsp/faq.html
http://www.esperanto.org.nz/jsp/jspfaq.html