Bugs item #467405, was opened at 2001-10-03 01:37
You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=104754&aid=467405&group_id=4754

Category: Tapestry
Group: bug
>Status: Closed
>Resolution: Rejected
Priority: 5
Submitted By: Richard Lewis-Shell (rlewisshell)
Assigned to: Howard Lewis Ship (hship)
Summary: inherited bindings don't update

Initial Comment:
If a component has a binding set at runtime (ie. after 
the pages have been loaded) then any inherited 
bindings utilized in that component remain as set by 
the PageLoader, rather than using the newly set 
parameter.

Would it be feasible to create an InheritedBinding 
(IBinding) implementation that pulls its object from 
its container on demand?  I think this would allow 
components to cache their bindings while allowing 
thier container to change its bindings.

Either that or someway of notifying contained 
components that a binding has changed.

----------------------------------------------------------------------

Comment By: Howard Lewis Ship (hship)
Date: 2001-10-24 15:28

Message:
Logged In: YES 
user_id=26816

I'm still opposed to bindings changing any time after page 
load.

However, as I under stand it, the essense of YOUR situation 
is you want to be able to set bindings for components that 
are in a Block retrieved via InsertBlock (the Block may be 
in a different page or component somewhere).

I'm still working on the details, but what if you could 
binding parameters to the InsertBlock (as informal 
parameters) and there way some way for the components in 
the inserted Block to access those parameters?

In other words, the InsertBlock could run through its 
informal parameters, generate a Map from them, and store 
the Map somewhere (i.e., in the request cycle).

A new helper bean would be able to expose that Map to the 
inserted components (inside the Block).

Effectively, this allows components on the page, that 
inserts the Block, to communicate with components inserted 
from within the Block.

Perhaps it should be a Map of bindings, rather than a map 
of values?  That would allow the inserted components to 
update through the Map/bindings to the properties of the 
inserting component.

Of course, this will require a *stack* structure, to handle 
degenerate cases where InsertBlocks include InsertBlocks.


----------------------------------------------------------------------

Comment By: Richard Lewis-Shell (rlewisshell)
Date: 2001-10-15 15:30

Message:
Logged In: YES 
user_id=167696

There is a problem with the copyInformalBindings() 
replacement posted earlier.  Here is a better version - 
this will set any parameters of the destination component 
to null if they were not copied from the source.  This 
prevents (or at least attempts to prevent) a reused 
component from using a binding from a previous use, but 
does require that all bindings be specified using this 
copyInformalBindings() seeing as any parameters not set 
this way will be set to null.  Here's the code:

    /**
     *  Copys all informal {@link IBinding bindings} from a 
source component
     *  to the destination component.  Informal bindings 
are bindings for
     *  informal parameters.  This will overwrite 
parameters (formal or
     *  informal) in the destination component if there is 
a naming conflict.
     *  Any parameters in the destination that are not set 
(copied) will be
     *  reset to null.
     */
    public static void copyInformalBindings(IComponent 
source, IComponent destination)
    {
        Collection sourceBindingNames = 
source.getBindingNames();
        Set remainingDestinationParameterNames = null;

        // if there are any source bindings, copy the 
informal ones
        if (sourceBindingNames != null) {
            ComponentSpecification sourceSpecification = 
source.getSpecification();
            for (Iterator i = sourceBindingNames.iterator
(); i.hasNext(); ) {
                String name = (String)i.next();

                // If not a formal parameter, then copy it 
over.
                if (sourceSpecification.getParameter(name) 
== null)
                {
                    IBinding binding = source.getBinding
(name);
                    //destination.setBinding(name, binding);
                    // we can't just set the binding, as 
any inherited bindings
                    // using that parameter-name won't be 
updated
                    propagateBinding(name, binding, 
destination);
                    if (remainingDestinationParameterNames 
== null)
                        remainingDestinationParameterNames 
= new HashSet(destination.getSpecification
().getParameterNames());
                    
remainingDestinationParameterNames.remove(name);
                }
            }
        }

        // if there are any destination bindings, that we 
haven't
        // set above, then reset them - otherwise they 
refer to
        // bindings from a previous copyInformalBindings() 
call
        // (typically from a different request/page)
        Collection destinationBindingNames = 
destination.getBindingNames();

        if (destinationBindingNames != null) {
            ComponentSpecification destinationSpecification 
= destination.getSpecification();
            for (Iterator i = 
remainingDestinationParameterNames.iterator(); i.hasNext
(); ) {
                String name = (String)i.next();
                propagateBinding(name, null, destination);
            }
        }
    }

    /**
     * Update the destination's binding (ie set it), and 
propagate
     * the binding to any inherited-bindings used by 
destination, or
     * its contained components.
     *
     * This is horribly inefficient - would be much better 
if a
     * coponent 'knew' where it had to propagate bindings if
     * they changed.
     */
    public static void propagateBinding(String 
parameterName, IBinding binding, IComponent destination) {
        if (destination == null || destination.getBinding
(parameterName) == binding) return; // don't propagate if 
we aren't changing anything

        destination.setBinding(parameterName, binding);

        ComponentSpecification destinationSpecification = 
destination.getSpecification();

        for (Iterator j = 
destinationSpecification.getComponentIds().iterator(); 
j.hasNext(); ) {
            String id = (String)j.next();

            ContainedComponent containedComponent = 
destinationSpecification.getComponent(id);
            for (Iterator k = 
containedComponent.getBindingNames().iterator(); k.hasNext
(); ) {
                String bindingName = (String)k.next();

                BindingSpecification bindingSpecification = 
containedComponent.getBinding(bindingName);
                if (bindingSpecification.getType().equals
(BindingType.INHERITED) && bindingSpecification.getValue
().equals(parameterName)) {
                    propagateBinding(bindingName, binding, 
destination.getComponent(id));
                }
            }

        }
    }


----------------------------------------------------------------------

Comment By: Richard Lewis-Shell (rlewisshell)
Date: 2001-10-04 17:05

Message:
Logged In: YES 
user_id=167696

But in this case, I am not trying to modify bindings/page-
structure for future requests - I can see your concern with 
this.

Instead, I am trying to modify bindings for the current 
request only (the one with the modified InsertBlock) - I 
was under the impression that the objects (pages, 
components, would be 'stable' (ie. jvm/pool-wise) 
throughout the request cycle.  It is true that the bindings 
that are being changed are on a different page, but they 
are only the bindings contained within the ParameterBlock 
(working name for modified Block component that allows 
informal bindings) component - those components are only 
expected to work when used with an InsertParameterBlock 
(working name for component which allows informal bindings 
and copies them using method posted earlier to the 
component in the ParameterBlock) which always supplied 
working bindings during the request cycle anyway.

The problem with my example is that it doesn't use much of 
Tapestry - only enough to prove that this can work.  Can 
you suggest something I can do to it to show how changing 
these bindings will cause problems?

Thanks for your time/help so far...

----------------------------------------------------------------------

Comment By: Howard Lewis Ship (hship)
Date: 2001-10-04 06:56

Message:
Logged In: YES 
user_id=26816

I realized the big problem this morning.

Pages *have to* be reconstructable from the specification.

In the scheme you're evolving, page bindings are too 
maleable.  However, changing bindings on one page instance 
won't affect other page instances in the same JVM, never 
mind in another server's JVM.

Subsequent requests from the same client may be in the same 
JVM, but against a different instance, or against a 
different JVM.

Tapestry makes assumptions, that should be made more 
explicit, that bindings are "locked" after a certain point; 
that is, after the page loads.

To allow pages to change bindings will break the whole 
pooling mechanism.

----------------------------------------------------------------------

Comment By: Richard Lewis-Shell (rlewisshell)
Date: 2001-10-04 02:34

Message:
Logged In: YES 
user_id=167696

Here is an implementation of Tapestry.copyInformalBindings
() that works as I believe it should.  As the comments 
suggest, it is by no means efficient - some sort of 
listener, or cache would be needed for that (as per 
previous suggestions).

    /**
     *  Copys all informal {@link IBinding bindings} from a 
source component
     *  to the destination component.  Informal bindings 
are bindings for
     *  informal parameters.  This will overwrite 
parameters (formal or
     *  informal) in the
     *  destination component if there is a naming conflict.
     *
     *
     */
    public static void copyInformalBindings(IComponent 
source, IComponent destination)
    {
        Collection names = source.getBindingNames();

        if (names == null)
            return;

        ComponentSpecification specification = 
source.getSpecification();
        Iterator i = names.iterator();

        while (i.hasNext())
        {
            String name = (String)i.next();

            // If not a formal parameter, then copy it over.

            if (specification.getParameter(name) == null)
            {
                IBinding binding = source.getBinding(name);
                //destination.setBinding(name, binding);
                // we can't just set the binding, as any 
inherited bindings
                // using that parameter-name won't be 
updated
                propagateBinding(name, binding, 
destination);
            }
        }
    }

    /**
     * Update the destination's binding (ie set it), and 
propagate
     * the binding to any inherited-bindings used by 
destination, or
     * its contained components.
     *
     * This is horribly inefficient - would be much better 
if a
     * coponent 'knew' where it had to propagate bindings if
     * they changed.
     */
    public static void propagateBinding(String 
parameterName, IBinding binding, IComponent destination) {
        if (destination == null) return;

        destination.setBinding(parameterName, binding);

        ComponentSpecification destinationSpecification = 
destination.getSpecification();

        for (Iterator j = 
destinationSpecification.getComponentIds().iterator(); 
j.hasNext(); ) {
            String id = (String)j.next();

            ContainedComponent containedComponent = 
destinationSpecification.getComponent(id);
            for (Iterator k = 
containedComponent.getBindingNames().iterator(); k.hasNext
(); ) {
                String bindingName = (String)k.next();

                BindingSpecification bindingSpecification = 
containedComponent.getBinding(bindingName);
                if (bindingSpecification.getType().equals
(BindingType.INHERITED) && bindingSpecification.getValue
().equals(parameterName)) {
                    propagateBinding(bindingName, binding, 
destination.getComponent(id));
                }
            }

        }
    }


----------------------------------------------------------------------

Comment By: Richard Lewis-Shell (rlewisshell)
Date: 2001-10-03 17:08

Message:
Logged In: YES 
user_id=167696

Re: a) someone here (was it you Howard?) once pointed out 
that relative to the HTTP transmission overheads combined 
with the EJB overheads, the extra time taken to perform 
these lookups was not significant.  I wonder how much 
slower it would really be...

Re: b) if there really is an efficiency concern, then this 
would be the better approach as only those components that 
needed this dynamic behaviour would be 'affected'.  Perhaps 
components could register the parameter names they share 
with their container to prevent too much work when the 
bindings change.

How about c) then: modify Tapestry.copyInformalBindings() 
or AbstractComponent.setBinding() to check if any contained 
components are sharing the binding that is about to be 
replaced.

I'll drop a note in the open discussion forum that explains 
what I am trying to do that causes changing bindings - 
perhaps there is a better way...

----------------------------------------------------------------------

Comment By: Howard Lewis Ship (hship)
Date: 2001-10-03 07:32

Message:
Logged In: YES 
user_id=26816

For efficiency, Tapestry assumes that bindings are set-once 
and forget.  This allows inherited bindings to *share* the 
same bindings.  That is, at page load time, the page loader 
looks up the named binding in the container and uses the 
same instance in the contained component.

To accomplish this requires either a) performing that 
lookup on each access of the binding -or- b) implementing 
some system to tell child components when their bindings 
may have been affected (perhaps using JavaBeans property 
change listeners).  I'm iffy on both of these!

----------------------------------------------------------------------

You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=104754&aid=467405&group_id=4754

_______________________________________________________________

Don't miss the 2002 Sprint PCS Application Developer's Conference
August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm

_______________________________________________
Tapestry-developer mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/tapestry-developer

Reply via email to