From dev-return-100853-apmail-cocoon-dev-archive=cocoon.apache.org@cocoon.apache.org Fri Dec 05 15:33:03 2008 Return-Path: Delivered-To: apmail-cocoon-dev-archive@www.apache.org Received: (qmail 15663 invoked from network); 5 Dec 2008 15:33:00 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 5 Dec 2008 15:33:00 -0000 Received: (qmail 11648 invoked by uid 500); 5 Dec 2008 15:33:11 -0000 Delivered-To: apmail-cocoon-dev-archive@cocoon.apache.org Received: (qmail 11580 invoked by uid 500); 5 Dec 2008 15:33:11 -0000 Mailing-List: contact dev-help@cocoon.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: dev@cocoon.apache.org List-Id: Delivered-To: mailing list dev@cocoon.apache.org Delivered-To: moderator for dev@cocoon.apache.org Received: (qmail 38697 invoked by uid 99); 5 Dec 2008 11:28:53 -0000 X-ASF-Spam-Status: No, hits=2.2 required=10.0 tests=HTML_MESSAGE,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: local policy) X-DSPAM-Result: Innocent X-DSPAM-Processed: Fri Dec 5 12:41:47 2008 X-DSPAM-Confidence: 0.9997 X-DSPAM-Probability: 0.0000 X-DSPAM-Signature: 4939218b92322195815323 X-DSPAM-Factors: 27, X-Virus-Scanned: amavisd-new at X-Spam-Score: -0.491 X-Spam-Level: Message-ID: <16773065.51228480905897.JavaMail.root@arnica.local> Date: Fri, 5 Dec 2008 12:41:45 +0000 (UTC) From: Simone Gianni To: dev@cocoon.apache.org Subject: R: Re: [cocoon3] Stax Pipelines In-Reply-To: <49355F59.40406@apache.org> MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_3_13309332.1228480905895" X-Virus-Checked: Checked by ClamAV on apache.org X-Old-Spam-Status: No, score=-0.491 tagged_above=-10 required=6.6 tests=[ALL_TRUSTED=-1.44, AWL=0.406, DSPAM_HAM=-0.1, HTML_00_10=0.642, HTML_MESSAGE=0.001] ------=_Part_3_13309332.1228480905895 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hi all,=20 since Stax is an inversion of the call flow, what we have is an inversion o= f the advantages and disadvantages we had with SAX.=20 I'll try to explain it better. Suppose we have two schemas, one contains "L= ONG" elements, with lots of children and stuff inside, the other contains "= SHORT" elements, with just as attribute "id". Now suppose it is possible to= translate from one to the other, for example it could be that LONG stuff i= s stored on the database, and SHORT is a placeholder pointing to LONGs on t= he database.=20 Now, we want to write two transformers. One is SHORT to LONG, which will pe= rform some selects on the database and expand those SHORT into LONG. The ot= her one stores stuff on the database, and convert LONG to SHORT.=20 As we all know (the i18n transformer is a good example), in SAX, transformi= ng from LONG to SHORT is a pain, cause we need to keep the state between mu= ltiple calls. In our example, if the LONG to SHORT transformer is a SAX bas= ed one, we would need to buffer all the LONG content, then store it on the = DB and then emit a single SHORT. That buffering is our state.=20 Instead, this kind of transformation is quite easy in a Stax transformer, c= ause when we encounter a LONG we can just fetch all the data we need, and p= erform everything we need to do in a single method, without having to prese= rve the state across different calls. Such a transformer in Stax could be n= early stateless/threadsafe from an XML point of view (the database connecti= on would be state, but that's just for the sake of the example).=20 However suppose we are doing the SHORT to LONG translation. In this case, u= sing SAX is by fax simpler than Stax. In fact, when we encounter a SHORT, w= e can fetch stuff from the DB and start bombing the next handler in the pip= eline with elements as soon as they arrive from the DB. Doing it in Stax in= stead would require us to have a state, cause we would need to buffer data = from the DB, and serve that data to the subseguent calls from our Stax cons= umer until the buffer is empty. Exactly the opposite problem of a SAX pipel= ine.=20 The SAX part of these example is nothing new to Cocoon. We already have an = infrastructure for buffering SAX events when we need to in our transformers= , in extremis even building a DOM out of it (which we could consider the mo= st versatile and expensive form of buffering). Couldn't we just provide suc= h a buffer for those Stax based transformers when they need it?=20 This would be an intermediate solution, cause there would be an easy way to= keep the state during Stax calls (as it was for SAX, but the opposite way = around), it would still be a pure Stax based pipeline, buffering would be l= imited to the bare minimum required by the transformer, and could be avoide= d at all reimplementing the transformer with more complex state logic if ne= eded for performance reasons.=20 This is not a solution to the SAX<->Stax cooperation problem, but my two ce= nts on the "Is implementing a Stax based transformer easier or more complic= ated than a Sax one" discussion :)=20 Simone=20 ----- Messaggio originale -----=20 Da: Sylvain Wallez =20 A: dev@cocoon.apache.org=20 Posta Inviata: marted=C3=AC 2 dicembre 2008 17.16.25 GMT+0100 Europe/Berlin= =20 Oggetto: Re: [cocoon3] Stax Pipelines=20 Reinhard P=C3=B6tz wrote:=20 > I've had Stax pipelines on my radar for a rather long time because I=20 > think that Stax can simplify the writing of transformers a lot.=20 > I proposed this idea to Alexander Schatten, an assistant professor at=20 > the Vienna University of Technology and he then proposed it to his=20 > students.=20 >=20 > A group of four students accepted to work on this as part of their=20 > studies. Steven and I are coaching this group from October to January=20 > and the goal is to support Stax pipeline components in Cocoon 3.=20 >=20 > So far the students learned more about Cocoon 3, Sax, Stax and did some= =20 > performance comparisons. This week we've entered the phase where the=20 > students have to work on the actual Stax pipeline implementation.=20 >=20 > I asked the students to introduce themselves and also to present the=20 > current ideas of how to implement Stax pipelines. So Andreas, Killian,=20 > Michael and Jakob, the floor is yours!=20 >=20 I have spent some cycles on this subject and came to the surprising=20 conclusion that writing Stax _pipelines_ is actually rather complex.=20 A Stax transformer pulls events from the previous component in the=20 pipeline, which removes the need for the complex state machinery often=20 needed for SAX (push) transformers by transforming it in a simple=20 function call stack and local variables. This is the main interest of=20 Stax vs SAX.=20 But how does a transformer expose its result to the next component in=20 the chain so that this next component can also pull events in the Stax=20 style?=20 When it produces an event, a Stax transformer should put this event=20 somewhere so that it can be pulled and processed by the next component.=20 But pulling also means the transformer does not suspend its execution=20 since it continues pulling events from the previous component. This is=20 actually reflected in the Stax API which provides a pull-based=20 XMLStreamReader, but only a very SAX-like XMLStreamWriter.=20 So a Stax transformer is actually a pull input / push output component.=20 To allow the next component in the pipeline to be also push-based, there=20 are 3 solutions (at least this is what I came up with) :=20 Buffering=20 ---------=20 The XMLStreamWriter where the transformer writes to buffers all events=20 in a data structure similar to our XMLByteStreamCompiler, that can be=20 used as a XMLStreamReader by the next component in the chain. The=20 pipeline object then has to call some execute() method on every=20 component in the pipeline in sequence, after having provided them with=20 the proper buffer-based reader and writer.=20 Execution is single-threaded, which fits well with all the non=20 threadsafe classes and threadlocals we usually have in web applications,=20 but requires buffering and thus somehow defeats the purpose of=20 stream-based processing and can be simply not possible to process large=20 documents.=20 Note however that because it is single-threaded, we can work with two=20 buffers (one for input, one for output) that are reused whatever the=20 number of components in the pipeline.=20 Multithreading=20 --------------=20 Each component of the pipeline runs in a separate thread, and writes its=20 output into an event queue that is consumed asynchronously by the next=20 component in the pipeline. The event queue is presented as an=20 XMLStreamReader to the next component.=20 This approach requires very little buffering (and we can even have an=20 upper bound on the event queue size). It also uses nicely the parallel=20 proccessing capabilities of multi-core CPUs, although in web apps the=20 parallelism is also handled by concurrent http requests. This is=20 typically the approach that would be used with Erlang or Scala actors.=20 Multithreading has some issues though, since the servlet API more or=20 less implies that a single thread processes the request and we may have=20 some concurrency issues. Web app developers also take single threading=20 as a basic assumption and use threadlocals here and there.=20 This approach also prevents the reuse of char[] buffers as is usually=20 done by XML parsers since events are processed asychronously. All char[]=20 have to be copied, but this is a minor issue.=20 Continuations=20 -------------=20 When a transformer sends an event to the next component in the chain,=20 its execution is suspended and captured in a continuation. The=20 continuation of the next pipeline component is resumed until it has=20 consumed the event. We then switch back to the current component until=20 it produces an event, etc, etc.=20 This approach is single-threaded and so avoids the concurrency issues=20 mentioned above, and also avoids buffering. But there is certainly a=20 high overhead with the large number of continuation capturing/resuming.=20 This number can be reduced though is we have some level of buffering to=20 allow processing of several events in one capture/resume cycle.=20 It also requires all the bytecode of transfomers to be instrumented for=20 continuations, which in itself adds quite some memory and processing=20 overhead. Torsten also posted on this subject quite long ago [1].=20 Conclusion=20 ----------=20 All things considered, I came to the conclusion that a full Stax=20 pipeline either requires buffering to be reliable (but we're no more=20 streaming), or requires very careful inspection of all components for=20 multi-threading issues.=20 So in the end, Stax probably has to be considered as a helper _inside_ a=20 component to ease processing : buffer all SAX input, then pull the=20 received events to avoid complex state automata.=20 Looks like I'm in a "long mail" period and I hope I haven't lost anybody=20 here :-)=20 So, what do you think?=20 Sylvain=20 [1] http://vafer.org/blog/20060807003609=20 --=20 Sylvain Wallez - http://bluxte.net=20 ------=_Part_3_13309332.1228480905895 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hi all,
since Stax is an inversion of the call flow, what we= have is an inversion of the advantages and disadvantages we had with SAX.<= br>
I'll try to explain it better. Suppose we have two schemas, one cont= ains "LONG" elements, with lots of children and stuff inside, the other con= tains "SHORT" elements, with just as attribute "id". Now suppose it is poss= ible to translate from one to the other, for example it could be that LONG = stuff is stored on the database, and SHORT is a placeholder pointing to LON= Gs on the database.

Now, we want to write two transformers. One is S= HORT to LONG, which will perform some selects on the database and expand th= ose SHORT into LONG. The other one stores stuff on the database, and conver= t LONG to SHORT.

As we all know (the i18n transformer is a good exam= ple), in SAX, transforming from LONG to SHORT is a pain, cause we need to k= eep the state between multiple calls. In our example, if the LONG to SHORT = transformer is a SAX based one, we would need to buffer all the LONG conten= t, then store it on the DB and then emit a single SHORT. That buffering is = our state.

Instead, this kind of transformation is quite easy in a S= tax transformer, cause when we encounter a LONG we can just fetch all the d= ata we need, and perform everything we need to do in a single method, witho= ut having to preserve the state across different calls. Such a transformer = in Stax could be nearly stateless/threadsafe from an XML point of view (the= database connection would be state, but that's just for the sake of the ex= ample).

However suppose we are doing the SHORT to LONG translation. = In this case, using SAX is by fax simpler than Stax. In fact, when we encou= nter a SHORT, we can fetch stuff from the DB and start bombing the next han= dler in the pipeline with elements as soon as they arrive from the DB. Doin= g it in Stax instead would require us to have a state, cause we would need = to buffer data from the DB, and serve that data to the subseguent calls fro= m our Stax consumer until the buffer is empty. Exactly the opposite problem= of a SAX pipeline.

The SAX part of these example is nothing new to = Cocoon. We already have an infrastructure for buffering SAX events when we = need to in our transformers, in extremis even building a DOM out of it (whi= ch we could consider the most versatile and expensive form of buffering). C= ouldn't we just provide such a buffer for those Stax based transformers whe= n they need it?

This would be an intermediate solution, cause there = would be an easy way to keep the state during Stax calls (as it was for SAX= , but the opposite way around), it would still be a pure Stax based pipelin= e, buffering would be limited to the bare minimum required by the transform= er, and could be avoided at all reimplementing the transformer with more co= mplex state logic if needed for performance reasons.

This is not a s= olution to the SAX<->Stax cooperation problem, but my two cents on th= e "Is implementing a Stax based transformer easier or more complicated than= a Sax one" discussion :)

Simone



----- Messaggio orig= inale -----
Da: Sylvain Wallez <sylvain@apache.org>
A: dev@coco= on.apache.org
Posta Inviata: marted=C3=AC 2 dicembre 2008 17.16.25 GMT+0= 100 Europe/Berlin
Oggetto: Re: [cocoon3] Stax Pipelines

Reinhard = P=C3=B6tz wrote:
> I've had Stax pipelines on my radar for a rather l= ong time because I
> think that Stax can simplify the writing of tran= sformers a lot.
> I proposed this idea to Alexander Schatten, an assi= stant professor at
> the Vienna University of Technology and he then = proposed it to his
> students.
>
> A group of four studen= ts accepted to work on this as part of their
> studies. Steven and I = are coaching this group from October to January
> and the goal is to = support Stax pipeline components in Cocoon 3.
>
> So far the st= udents learned more about Cocoon 3, Sax, Stax and did some
> performa= nce comparisons. This week we've entered the phase where the
> studen= ts have to work on the actual Stax pipeline implementation.
>
>= I asked the students to introduce themselves and also to present the
&g= t; current ideas of how to implement Stax pipelines. So Andreas, Killian,> Michael and Jakob, the floor is yours!
>  

I have= spent some cycles on this subject and came to the surprising
conclusio= n that writing Stax _pipelines_ is actually rather complex.

A Stax t= ransformer pulls events from the previous component in the
pipeline, wh= ich removes the need for the complex state machinery often
needed for S= AX (push) transformers by transforming it in a simple
function call sta= ck and local variables. This is the main interest of
Stax vs SAX.
But how does a transformer expose its result to the next component in the chain so that this next component can also pull events in the Stax style?

When it produces an event, a Stax transformer should put thi= s event
somewhere so that it can be pulled and processed by the next co= mponent.
But pulling also means the transformer does not suspend its ex= ecution
since it continues pulling events from the previous component. = This is
actually reflected in the Stax API which provides a pull-based =
XMLStreamReader, but only a very SAX-like XMLStreamWriter.

So a = Stax transformer is actually a pull input / push output component.

T= o allow the next component in the pipeline to be also push-based, there are 3 solutions (at least this is what I came up with) :

Buffering<= br>---------
The XMLStreamWriter where the transformer writes to buffers= all events
in a data structure similar to our XMLByteStreamCompiler, t= hat can be
used as a XMLStreamReader by the next component in the chain= . The
pipeline object then has to call some execute() method on every <= br>component in the pipeline in sequence, after having provided them with <= br>the proper buffer-based reader and writer.

Execution is single-th= readed, which fits well with all the non
threadsafe classes and threadl= ocals we usually have in web applications,
but requires buffering and t= hus somehow defeats the purpose of
stream-based processing and can be s= imply not possible to process large
documents.

Note however that= because it is single-threaded, we can work with two
buffers (one for i= nput, one for output) that are reused whatever the
number of components= in the pipeline.

Multithreading
--------------
Each component= of the pipeline runs in a separate thread, and writes its
output into = an event queue that is consumed asynchronously by the next
component in= the pipeline. The event queue is presented as an
XMLStreamReader to th= e next component.

This approach requires very little buffering (and = we can even have an
upper bound on the event queue size). It also uses = nicely the parallel
proccessing capabilities of multi-core CPUs, althou= gh in web apps the
parallelism is also handled by concurrent http reque= sts. This is
typically the approach that would be used with Erlang or S= cala actors.

Multithreading has some issues though, since the servle= t API more or
less implies that a single thread processes the request a= nd we may have
some concurrency issues. Web app developers also take si= ngle threading
as a basic assumption and use threadlocals here and ther= e.

This approach also prevents the reuse of char[] buffers as is usu= ally
done by XML parsers since events are processed asychronously. All = char[]
have to be copied, but this is a minor issue.

Continuatio= ns
-------------
When a transformer sends an event to the next compon= ent in the chain,
its execution is suspended and captured in a continua= tion. The
continuation of the next pipeline component is resumed until = it has
consumed the event. We then switch back to the current component= until
it produces an event, etc, etc.

This approach is single-t= hreaded and so avoids the concurrency issues
mentioned above, and also = avoids buffering. But there is certainly a
high overhead with the large= number of continuation capturing/resuming.
This number can be reduced = though is we have some level of buffering to
allow processing of severa= l events in one capture/resume cycle.

It also requires all the bytec= ode of transfomers to be instrumented for
continuations, which in itsel= f adds quite some memory and processing
overhead. Torsten also posted o= n this subject quite long ago [1].


Conclusion
----------
A= ll things considered, I came to the conclusion that a full Stax
pipelin= e either requires buffering to be reliable (but we're no more
streaming= ), or requires very careful inspection of all components for
multi-thre= ading issues.

So in the end, Stax probably has to be considered as a= helper _inside_ a
component to ease processing : buffer all SAX input,= then pull the
received events to avoid complex state automata.

= Looks like I'm in a "long mail" period and I hope I haven't lost anybody here :-)

So, what do you think?

Sylvain

[1] http://v= afer.org/blog/20060807003609

--
Sylvain Wallez - http://bluxte.n= et

------=_Part_3_13309332.1228480905895--