cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Brett McLaughlin <bmcla...@algx.net>
Subject Re: Using Cocoon from other Java code (very long)
Date Fri, 17 Dec 1999 15:24:03 GMT
Stefano Mazzocchi wrote:
> 
> I wish we had more people like this guy, man!
> 
> Way to go, Brett!

I plan on adding a lot more... hopefully you'll all soon be screaming to
give me commit access because I'm sending in so many patches and
enhancements ;-)

> 
> 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?

This is what you refer to later.  Turbine gets a HttpServletRequest and
has no way to modify that (no setXXX() methods) so it doesn't help to do
an include() or forward().  I want to create my own request.

> 
> >         So here is my solution.
> >
> > First: With all the changes, everything in Cocoon WORKS AS IS.
> 
> these are the magic words :)

I thought you'd like that.

> 
> > 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.

Great, I use this and it works nicely.

> 
> > 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.

Bad example.  The better example is the producer=<producer> parameter
that will almost certainly not be in the request that Turbine gets, but
should be in the request that Cocoon gets.  Make sense?

> 
> > 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.

So it sounds like you are willing to commit at least the
Engine.java.diff getInstance() methods, right?  Plus the
Cocoon.java.diff which uses the new Engine.getInstance() method.  Let me
know if there are still problems there....

> >
> > 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)

This is OK.  We (or at least I) understand that the API is screwed, and
am willing to do some strange things to get around it for the sake of
keeping things like Cocoon "pure".

> 
> 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.

I see.... I wish I had thought of that (stupid of me, really).  Yes, I
agree that it makes more sense to dummy that up, allowing
Producer.java.diff, AbstractProducer.java.diff, and the rest of
Engine.java.diff to not have to be applied.  In addition, I will revise
my examples to use this model.

> 
> 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 wondered what that was about... it didn't make sense when I first
looked at it, but now I get the idea.

> 
> 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)

Well, it isn't so much.  We already are able to grab the contents and
write to the ServletOutputStream, so this isn't too bad.  Cleaner than
having the extra Hashtable, IMO, so I'm OK with it.

> 
> What do you think?
> 

I think you're the man ;-)  So can you commit those diffs (or parts of
them) so I can start using this stuff?  I don't want to commit examples
to Turbine until everyone can use a Cocoon that works with them... since
we aren't doing the larger Hashtable changes, and it's just a Engine
change, maybe this could get into 1.6?  It would really help newbies at
Turbine to be able to use a full release, rather than have to get Cocoon
from CVS...

Thanks,
Let me know if there is anything else I can help with to get this going
(or other help, too)

-Brett


> --
> 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