cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stefano Mazzocchi <stef...@apache.org>
Subject Re: Using Cocoon from other Java code (very long)
Date Fri, 17 Dec 1999 00:20:37 GMT
I wish we had more people like this guy, man!

Way to go, Brett!

Brett McLaughlin wrote:
> 
> All-
> 
>         First off, let me apologize up front for sending this to the list and
> some individuals on Turbine; however, this is a really significant
> change I am trying to get in place.  That being said...
> 
>         I have been working on allowing Cocoon to be used from more than just
> the Cocoon front-end servlet.  For example, in Turbine, screens may
> generate all their (XML) content, and want to send those screens through
> to Cocoon to process.  Right now (at least with the code in CVS) this is
> impossible, particularly in JSDK 2.0 with no RequestDispatcher.include()
> or RequestDispatcher.forward().  In addition, there are some very tricky
> issues to do with Cocoon expecting some things in the HttpServletRequest
> that another application couldn't put in there on the fly (there are no
> addXXX or setXXX methods on ServletRequest).

For example?
 
>         So here is my solution.
> 
> First: With all the changes, everything in Cocoon WORKS AS IS.  

these are the magic words :)

> Again, not a single line of producer/processor custom code has to change.
> 
> So here it is.  The first things that happens is that Cocoon starts up
> (via init() in the servlet) and initializes the Cocoon Engine.  Now this
> is something we want shared - in other words, nothing but the Cocoon
> servlet should ever start up the engine, like with a
> Class.forName().newInstance(), because all the parameters aren't set,
> and Cocoon handles that nicely.  However, there is never a need for more
> than one Engine instance for a given JVM/servlet zone.  So based on
> that, I made Engine a singleton.  Instead of doing:
> 
> Engine engine = new Engine(confs,
> this.getServletConfig().getServletContext());
> 
> it now does
> 
> Engine engine = Engine.getInstance(confs,
> this.getServletConfig().getServletContext());
> 
> The getInstance() method takes care of instantiating the class, and
> storing an internal reference to it.  Now, another application wanting
> to use Cocoon processing can do:
> 
> Engine cocoonEngine = Engine.getInstance();
> 
> This no-args version throws an exception if the Cocoon servlet has not
> already initialized the engine with all of the correct information (so
> Cocoon must be running as a servlet for any of this to work).
> 
> So now, I can call <code>cocoonEngine.handle(req, res);</code> from
> other applications, and get the benefits of Cocoon processing.  This is
> the first step.

+1, I failed to apply the Singleton pattern to Engine, very good
solution.
 
> And again, Cocoon works as is, no changes to modules, producers,
> processors.
> 
> Next: The next problem is that of the HttpServletRequest object.  As I
> mentioned, all this doesn't help much, because the request coming in to
> Turbine or some other object doesn't contain the information that Cocoon
> needs.  Also, for a standalone application, even if they get a new
> instance of HttpServletRequest to meet the method parameters, it doesn't
> tell Cocoon what it needs to know.  So how to address this?  Well, lots
> of changes in Engine.java, but none that are as bad as you may think.
> 
> Allow a new method: <code>public void handle(HttpServletRequest request,
> HttpServletResponse response, Hashtable params) throws Exception;</code>
> 
> This is just what it looks like - another version of handle that allows
> the passing in of a Hashtable of additional parameters.  This is what
> other applications should be able to do to specify to Cocoon what to
> use.  However, the request object _always_ takes precedence.  If params
> is null, it is pretty much ignored.  I made changes to all the helper
> functions like getKey() to check the request, and if the key was not
> found and the Hashtable wasn't null, check there.  Then behave
> normally.  This basically takes care of allowing other applications to
> pass in to Cocoon things to process on (like the "user-Agent", the
> producer to use, etc.)

who come the user-Agent is not available thru the request? I don't get
this.
 
> You will see all of these changes in Engine.java (diff included), but
> you will notice that in total, for already running apps using Cocoon as
> it was originally designed, it only adds about 5 cycles - one for each
> <code>if</code> statement to check on the Hashtable being null.  When
> it's null, it goes on.  And these checks only occur if a parameter isn't
> specified in the request object, so when that happens, there is almost
> no difference at all.  The only complex issue here is the cache.  It is
> tough to determine if a page is in cache when the page is being
> requested by a combination of the request object and the Hashtable
> parameters.  For now, I don't check for cache if Hashtable parameters
> are present.  This could be done later.
> 
> I also draw the line after the initial producer is used for checking
> Hashtable params.  Things like specifying a processor can all be done
> within the producer in the document, so for now (and simplicity), the
> producer should take care of that.  This also speeds processing up, as
> we aren't checking anything differently after the producer changepoint.
> 
> Next:
> So all that's left is the producer portion of things.  Presumably, an
> application would create it's own content and shove it into the
> Hashtable.  It could alternatively pass instructions in, and write a
> producer that would receive those instructions and do all sorts of other
> interesting things.  After the document is parsed, it is back to normal
> Cocoon operation, and we are in good shape.
> 
> So I had to make some producer changes to accomodate this.  Again, all
> existing producers will work AS IS (I tested this numerous times).
> 
> First, there needs to be a way for ProducerFactory to see if a producer
> was requested in the Hashtable params.  THis was a minor change, similar
> to the Engine - the params are only checked if there is nothing
> requested in the HttpServletRequest, and the params are not null.
> 
> Then, a producer should have a getStream(HttpServletRequest,
> HttpServletResponse, Hashtable) to pass in the additional
> application-set parameters and getDocument(<same three params>) that
> overload the current implementations.  This allows the producer to get
> access to all the things set by the application.
> 
> Finally, the AbstractProducer makes this all work like a dream.  The
> getDocument method stays pretty much as it, just add an overloaded
> version to accept a Hashtable.  That then calls getStream(request,
> response, params).  This method, because it is new, could cause
> producers that already exist problems, but the nice OO design saved the
> day (nice job, Stefano & Co.).  I added a method getStream(request,
> response, params) into AbstractProducer that takes the request, simply
> drops off the Hashtable, and calls and returns the result of
> getStream(request, response).  This means that all existing producers
> work as is, same methods get called, etc.
> 
> _however_, I can now easily write a Producer, say StreamProducer, that
> overrides that getStream() with the Hashtable and pulls out information
> from that, creates a document, and returns it.  Viola!  Cocoon from a
> non-Cocoon application.
> 
> Please look these over, it really works great!  I included all the
> needed diffs, and will work on an example Producer and sample
> application to show how it is used.  Sorry for the long post and the
> multiple diffs.
> 
> I know this many seems like a lot of changes, but when you look at the
> code, it is really minor stuff - the design allowed for it all very
> easily.  Comments?

I do like the first part. I honestly find the second a dirty hack and,
yes, I think I have a better solution even if requires more processing
on the caller part (but this is not my fault, it's the Servlet API
model)

You said that HttpServletRequest didn't provide you with the ability to
set parameters. True, but only if you used the object that was given to
you. If you create your own implementation of that interface you are in
good shape and you can "fake" a call to Cocoon like you were the servlet
engine, here it goes

  servlet engine ---(request1)---> turbine ---(request2)---> cocoon

this is exactly how command line operation works

  command line -(fakerequest)--> cocoon

take a look at EngineWrapper.java and you'll know what I mean.

Also, Cocoon is already ready for that: the ProducerFromRequest takes
care of producing XML reading the request input stream. In turbine (or
any other application) you'll create the object and fill it with the
input stream you want to be processed.

I know this may look like a dirty hack from the turbine perspective, but
it's a problem in servlet piping, something that Servlet 3.0 is supposed
to address (even if we didn't yet start talking about it)

What do you think?

-- 
Stefano Mazzocchi      One must still have chaos in oneself to be
                          able to give birth to a dancing star.
<stefano@apache.org>                             Friedrich Nietzsche



Mime
View raw message