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