You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by Gianugo Rabellino <gi...@apache.org> on 2004/04/16 18:56:26 UTC

[cforms] Weird behaviour of flow and fb:javascript binding

We are currently building a cforms based application with fairly decent 
complexity level. Our (rather convoluted but, take my word for that, 
legit) business logic requires the usage of an Avalon component during 
the binding phase, so we thought to use the fb:javascript binding 
method, hoping to find (even if not explicitely documented) the good old 
FOM, together with the cocoon object.

That was indeed the case, and we were cheerfully using logic like the 
one in the following snippet:

<fb:load-form>
    var validation = cocoon.getComponent("bindinghelper");
    var today = new Packages.java.util.GregorianCalendar();
    var executionDate = validation.getValidExecutionDate(today);
    widget.setValue(executionDate.getTime());
</fb:load-form>

At a certain point, though, we mangled the form *definition* and 
modified a selection list so that it was taking its data from a cocoon:/ 
pipeline instead than from a static file (which we used during 
prototyping). Such pipeline, in turns, calls the flow which sendPage()s 
to a JXTG (data are in a business object we need to stream):

     <map:match pattern="lastTransfer">
         <map:call function="lastTransferList"/>
     </map:match>

     <map:match pattern="lastTransferList">
       <map:generate type="jxtg" src="resources/jxt/transfer-list.jxt"/>
       <map:transform src="resources/xsl/transfer-list2fdsel-list.xsl"/>
       <map:serialize type="xml"/>
     </map:match>

(now, talking about tangled web of pipelines calling flow calling 
pipelines... but anyway)

Here is where crap happens. In our binding phase the cocoon object isn't 
there anymore:

"file:/Users/gianugo/dev/src/newcorporate/src/build/webapp/italiantransfer/resources/forms/italian-transfer-bind-bean.xml", 
line 5: uncaught JavaScript exception:
at insertItalianTransfer 
(file:/Users/gianugo/dev/src/newcorporate/src/build/webapp/italiantransfer/flow/italianTransfer.js, 
Line 42)
at  (resource://org/apache/cocoon/forms/flow/javascript/v2/Form.js, Line 
158):
ReferenceError: "cocoon" is not defined. 
(file:/Users/gianugo/dev/src/newcorporate/src/build/webapp/italiantransfer/resources/forms/italian-transfer-bind-bean.xml; 
line 5)


and we are unable to run our binding code. Debugging 
o.a.c.f.util.JavascriptHelper, it turns out that the parent scope in our 
first scenario is a FOM, while the insertion in the definition of the 
dynamic list, (apparently) causing another flow call to happen, leaves 
an empty NativeObject in place of the FOM.

OK, I hope you're still with me until now. :-) Questions follow:

1. is this an expected behaviour?

2. would it be possible/make sense to ensure that even javascript 
bindings get a FOM object?

3. if not, what is the alternative? Writing a custom binding? Really? :-/

Thanks for your time on this. I'm really banging my head against this 
issue and can't quite see a way out as of now.

Ciao,

-- 
Gianugo Rabellino
Pro-netics s.r.l. -  http://www.pro-netics.com
Orixo, the XML business alliance - http://www.orixo.com
     (Blogging at: http://www.rabellino.it/blog/)


Re: [cforms] Weird behaviour of flow and fb:javascript binding

Posted by Gianugo Rabellino <gi...@apache.org>.
On Apr 25, 2004, at 2:14 PM, Sylvain Wallez wrote:

> Gianugo Rabellino wrote:
>>
>> At a certain point, though, we mangled the form *definition* and 
>> modified a selection list so that it was taking its data from a 
>> cocoon:/ pipeline instead than from a static file (which we used 
>> during prototyping). Such pipeline, in turns, calls the flow which 
>> sendPage()s to a JXTG (data are in a business object we need to 
>> stream):
>
>
> <snip/>
>
> I found the cause of the problem.
>
> I modified FlowHelper and FOM_JavaScriptFlowHelper so that they use 
> the object model. I also moved to private all static strings defining 
> storage keys as I found some direct references to them to store data 
> as request attributes that violate the encapsulation provided by the 
> setters in these two classes. This may cause incompatibilities in 
> external code that does the same, but there's no way around it if we 
> want the use of setters to be enforced, which hides the storage 
> location for this data.
>
> This should solve your problem. Please cross-check!
>

Thanks so much Sylvain. Cross-checking will require a bit of time since 
it involves mangling a quite complex app (we had to put together a 
workaround), but I'll let you know ASAP!

Ciao,

-- 
Gianugo Rabellino
Pro-netics s.r.l. -  http://www.pro-netics.com
Orixo, the XML business alliance: http://www.orixo.com


Re: [cforms] Weird behaviour of flow and fb:javascript binding

Posted by Sylvain Wallez <sy...@apache.org>.
Gianugo Rabellino wrote:

> We are currently building a cforms based application with fairly 
> decent complexity level. Our (rather convoluted but, take my word for 
> that, legit) business logic requires the usage of an Avalon component 
> during the binding phase, so we thought to use the fb:javascript 
> binding method, hoping to find (even if not explicitely documented) 
> the good old FOM, together with the cocoon object.
>
> That was indeed the case, and we were cheerfully using logic like the 
> one in the following snippet:
>
> <fb:load-form>
>    var validation = cocoon.getComponent("bindinghelper");
>    var today = new Packages.java.util.GregorianCalendar();
>    var executionDate = validation.getValidExecutionDate(today);
>    widget.setValue(executionDate.getTime());
> </fb:load-form>
>
> At a certain point, though, we mangled the form *definition* and 
> modified a selection list so that it was taking its data from a 
> cocoon:/ pipeline instead than from a static file (which we used 
> during prototyping). Such pipeline, in turns, calls the flow which 
> sendPage()s to a JXTG (data are in a business object we need to stream):


<snip/>

I found the cause of the problem.

The flowscript scope is stored in a request attribute (see 
FOM_JavaScriptFlowHelper) and this attribute is cleared upon termination 
of the script execution to prevent any further usage after the script 
execution is finished.

The problem comes from the fact that request attributes are shared 
between the "real" request and all internal requests that are created to 
answer the real one. This means that when your selection list pipeline 
is executed :
1/ the original scope (from form.showForm) is overriden by the scope for 
the selection list
2/ when the selection list has been generated, the request attribute is 
cleared, and there is therefore no more "cocoon" object in the request 
for subsequent js bindings.

The solution is to use a storage means that is independent for each of 
the various internal requests used to produce the response. In Cocoon, 
this is the object model. It also has the additional benefit that a 
child requests starts with a copy of the parent's object model and 
locally overrides it (works like InheritableThreadLocal)

The problem is potentially the same for every other data issued by the 
flowscript (continuation, view data, request, etc).

I modified FlowHelper and FOM_JavaScriptFlowHelper so that they use the 
object model. I also moved to private all static strings defining 
storage keys as I found some direct references to them to store data as 
request attributes that violate the encapsulation provided by the 
setters in these two classes. This may cause incompatibilities in 
external code that does the same, but there's no way around it if we 
want the use of setters to be enforced, which hides the storage location 
for this data.

This should solve your problem. Please cross-check!

Sylvain

-- 
Sylvain Wallez                                  Anyware Technologies
http://www.apache.org/~sylvain           http://www.anyware-tech.com
{ XML, Java, Cocoon, OpenSource }*{ Training, Consulting, Projects }


Re: [cforms] Weird behaviour of flow and fb:javascript binding

Posted by Christopher Oliver <re...@verizon.net>.
Sylvain Wallez wrote:

> Christopher Oliver wrote:
>
>> I can't say that I fully understand your problem, but I just looked 
>> at o.a.c.f.util.JavascriptHelper, and that appears to have some major 
>> bugs. If it isn't called from a flow script it uses a static 
>> JavaScript object as the top level scope (where JS global variables 
>> are created), but does not even synchronize access to it. That means 
>> any global variables you set in one of your Javascript snippets 
>> (intentionally or accidentally) will be visible to all users and you 
>> will see undefined behavior if multiple threads read and write the 
>> same variables.
>
>
>
> Learning the internals of Rhino and how this scope and context 
> machinery works is not an easy task :-/


See http://www.mozilla.org/rhino/scopes.html.

>
> So the solution is to create a new scope for each call, right? Also, 
> instead of creating an intermediate scope to hold snippet-specific 
> data, I was think of using functions where this intermediate scope 
> would be function parameters. That way "global" variables in the 
> snippet actually are local variables of the function.
>
>> If it is called indirectly from a flow script (i.e. from sendPage*()) 
>> you share global variables with the flow script (that's why you could 
>> access the "cocoon" object in some cases but not others). But this is 
>> also bad because it makes bugs related to accidentally overwriting 
>> global variables very hard to find.
>
>
>
> I consider this the availability of the cocoon object and global 
> flowscript variables an essential feature, as form event listeners are 
> an integral part of the controller.
>
See below.

>> However, as a hack for the time being, you could probably set the 
>> request attribute it uses to obtain the flow script global scope 
>> yourself before calling the binding.
>>
>> As regards evaluating JavaScript snippets in form definition and 
>> binding files, I don't think the flowscript global scope should used. 
>> Rather, a special scope should be created (- and unique per thread) 
>> in which to evaluate those snippets. The binding framework and form 
>> framework can make available appropriate Cocoon system objects in 
>> this scope if that is required (such as an object that allows you to 
>> get an avalon component).
>
>
>
> Mmmh... how would this "cocoon" object be different from the one in 
> flowscript?
>
>
Depends on the requirements of the Form and Binding frameworks (in other 
words a design is needed). But, for example, I think we can all agree it 
should not have a "sendPageAndWait()" function.

Chris

Re: [cforms] Weird behaviour of flow and fb:javascript binding

Posted by Sylvain Wallez <sy...@apache.org>.
Christopher Oliver wrote:

> I can't say that I fully understand your problem, but I just looked at 
> o.a.c.f.util.JavascriptHelper, and that appears to have some major 
> bugs. If it isn't called from a flow script it uses a static 
> JavaScript object as the top level scope (where JS global variables 
> are created), but does not even synchronize access to it. That means 
> any global variables you set in one of your Javascript snippets 
> (intentionally or accidentally) will be visible to all users and you 
> will see undefined behavior if multiple threads read and write the 
> same variables.


Learning the internals of Rhino and how this scope and context machinery 
works is not an easy task :-/

So the solution is to create a new scope for each call, right? Also, 
instead of creating an intermediate scope to hold snippet-specific data, 
I was think of using functions where this intermediate scope would be 
function parameters. That way "global" variables in the snippet actually 
are local variables of the function.

> If it is called indirectly from a flow script (i.e. from sendPage*()) 
> you share global variables with the flow script (that's why you could 
> access the "cocoon" object in some cases but not others). But this is 
> also bad because it makes bugs related to accidentally overwriting 
> global variables very hard to find.


I consider this the availability of the cocoon object and global 
flowscript variables an essential feature, as form event listeners are 
an integral part of the controller.

> However, as a hack for the time being, you could probably set the 
> request attribute it uses to obtain the flow script global scope 
> yourself before calling the binding.
>
> As regards evaluating JavaScript snippets in form definition and 
> binding files, I don't think the flowscript global scope should used. 
> Rather, a special scope should be created (- and unique per thread) in 
> which to evaluate those snippets. The binding framework and form 
> framework can make available appropriate Cocoon system objects in this 
> scope if that is required (such as an object that allows you to get an 
> avalon component).


Mmmh... how would this "cocoon" object be different from the one in 
flowscript?

Sylvain

-- 
Sylvain Wallez                                  Anyware Technologies
http://www.apache.org/~sylvain           http://www.anyware-tech.com
{ XML, Java, Cocoon, OpenSource }*{ Training, Consulting, Projects }


Re: [cforms] Weird behaviour of flow and fb:javascript binding

Posted by Christopher Oliver <re...@verizon.net>.
I can't say that I fully understand your problem, but I just looked at 
o.a.c.f.util.JavascriptHelper, and that appears to have some major bugs. 
If it isn't called from a flow script it uses a static JavaScript object 
as the top level scope (where JS global variables are created), but does 
not even synchronize access to it. That means any global variables you 
set in one of your Javascript snippets (intentionally or accidentally) 
will be visible to all users and you will see undefined behavior if 
multiple threads read and write the same variables. If it is called 
indirectly from a flow script (i.e. from sendPage*()) you share global 
variables with the flow script (that's why you could access the "cocoon" 
object in some cases but not others). But this is also bad because it 
makes bugs related to accidentally overwriting global variables very 
hard to find.

However, as a hack for the time being, you could probably set the 
request attribute it uses to obtain the flow script global scope 
yourself before calling the binding.

As regards evaluating JavaScript snippets in form definition and binding 
files, I don't think the flowscript global scope should used. Rather, a 
special scope should be created (- and unique per thread) in which to 
evaluate those snippets. The binding framework and form framework can 
make available appropriate Cocoon system objects in this scope if that 
is required (such as an object that allows you to get an avalon component).

Chris

Gianugo Rabellino wrote:

> We are currently building a cforms based application with fairly 
> decent complexity level. Our (rather convoluted but, take my word for 
> that, legit) business logic requires the usage of an Avalon component 
> during the binding phase, so we thought to use the fb:javascript 
> binding method, hoping to find (even if not explicitely documented) 
> the good old FOM, together with the cocoon object.
>
> That was indeed the case, and we were cheerfully using logic like the 
> one in the following snippet:
>
> <fb:load-form>
>    var validation = cocoon.getComponent("bindinghelper");
>    var today = new Packages.java.util.GregorianCalendar();
>    var executionDate = validation.getValidExecutionDate(today);
>    widget.setValue(executionDate.getTime());
> </fb:load-form>
>
> At a certain point, though, we mangled the form *definition* and 
> modified a selection list so that it was taking its data from a 
> cocoon:/ pipeline instead than from a static file (which we used 
> during prototyping). Such pipeline, in turns, calls the flow which 
> sendPage()s to a JXTG (data are in a business object we need to stream):
>
>     <map:match pattern="lastTransfer">
>         <map:call function="lastTransferList"/>
>     </map:match>
>
>     <map:match pattern="lastTransferList">
>       <map:generate type="jxtg" src="resources/jxt/transfer-list.jxt"/>
>       <map:transform src="resources/xsl/transfer-list2fdsel-list.xsl"/>
>       <map:serialize type="xml"/>
>     </map:match>
>
> (now, talking about tangled web of pipelines calling flow calling 
> pipelines... but anyway)
>
> Here is where crap happens. In our binding phase the cocoon object 
> isn't there anymore:
>
> "file:/Users/gianugo/dev/src/newcorporate/src/build/webapp/italiantransfer/resources/forms/italian-transfer-bind-bean.xml", 
> line 5: uncaught JavaScript exception:
> at insertItalianTransfer 
> (file:/Users/gianugo/dev/src/newcorporate/src/build/webapp/italiantransfer/flow/italianTransfer.js, 
> Line 42)
> at  (resource://org/apache/cocoon/forms/flow/javascript/v2/Form.js, 
> Line 158):
> ReferenceError: "cocoon" is not defined. 
> (file:/Users/gianugo/dev/src/newcorporate/src/build/webapp/italiantransfer/resources/forms/italian-transfer-bind-bean.xml; 
> line 5)
>
>
> and we are unable to run our binding code. Debugging 
> o.a.c.f.util.JavascriptHelper, it turns out that the parent scope in 
> our first scenario is a FOM, while the insertion in the definition of 
> the dynamic list, (apparently) causing another flow call to happen, 
> leaves an empty NativeObject in place of the FOM.
>
> OK, I hope you're still with me until now. :-) Questions follow:
>
> 1. is this an expected behaviour?
>
> 2. would it be possible/make sense to ensure that even javascript 
> bindings get a FOM object?
>
> 3. if not, what is the alternative? Writing a custom binding? Really? :-/
>
> Thanks for your time on this. I'm really banging my head against this 
> issue and can't quite see a way out as of now.
>
> Ciao,
>