You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2012/05/12 08:53:17 UTC
svn commit: r1337454 - in /tapestry/tapestry5/branches/5.3/tapestry-core/src:
main/java/org/apache/tapestry5/
main/java/org/apache/tapestry5/internal/structure/
main/java/org/apache/tapestry5/internal/transform/
main/java/org/apache/tapestry5/internal/...
Author: hlship
Date: Sat May 12 06:53:16 2012
New Revision: 1337454
URL: http://svn.apache.org/viewvc?rev=1337454&view=rev
Log:
TAP5-1929: Performance improvements
Rework page loading and lazily initialization inside InternalComponentResourcesImpl to avoid some synchronization hotspots
Added:
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
Modified:
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectComponentWorker.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
tapestry/tapestry5/branches/5.3/tapestry-core/src/main/resources/org/apache/tapestry5/internal/structure/StructureStrings.properties
tapestry/tapestry5/branches/5.3/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import org.apache.tapestry5.ioc.Messages
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.model.ComponentModel;
import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.runtime.PageLifecycleCallbackHub;
import org.apache.tapestry5.runtime.PageLifecycleListener;
import java.lang.annotation.Annotation;
@@ -76,11 +77,11 @@ public interface ComponentResources exte
/**
* Returns an embedded component, given the component's id.
- *
+ *
* @param embeddedId
- * selects the embedded component (case is ignored)
+ * selects the embedded component (case is ignored)
* @throws org.apache.tapestry5.ioc.util.UnknownValueException
- * if this component does not contain a component with the given id
+ * if this component does not contain a component with the given id
*/
Component getEmbeddedComponent(String embeddedId);
@@ -92,11 +93,11 @@ public interface ComponentResources exte
/**
* Obtains an annotation provided by a parameter.
- *
+ *
* @param parameterName
- * name of parameter to search for the annotation
+ * name of parameter to search for the annotation
* @param annotationType
- * the type of annotation
+ * the type of annotation
* @return the annotation if found or null otherwise
*/
<T extends Annotation> T getParameterAnnotation(String parameterName, Class<T> annotationType);
@@ -104,9 +105,9 @@ public interface ComponentResources exte
/**
* Indentifies all parameters that are not formal parameters and writes each as a attribute/value pair into the
* current element of the markup writer.
- *
+ *
* @param writer
- * to which {@link MarkupWriter#attributes(Object[]) attributes} will be written
+ * to which {@link MarkupWriter#attributes(Object[]) attributes} will be written
*/
void renderInformalParameters(MarkupWriter writer);
@@ -120,9 +121,9 @@ public interface ComponentResources exte
* with property bindings, and is used to determine the actual type of the property, rather than the type of
* parameter (remember that type coercion automatically occurs, which can mask significant differences between the
* parameter type and the bound property type).
- *
+ *
* @param parameterName
- * used to select the parameter (case is ignored)
+ * used to select the parameter (case is ignored)
* @return the type of the bound parameter, or null if the parameter is not bound
* @see Binding#getBindingType()
*/
@@ -130,54 +131,65 @@ public interface ComponentResources exte
/**
* Returns an annotation provider, used to obtain annotations related to the parameter.
- *
+ *
* @param parameterName
- * used to select the parameter (case is ignored)
+ * used to select the parameter (case is ignored)
* @return the annotation provider, or null if the parameter is not bound
*/
AnnotationProvider getAnnotationProvider(String parameterName);
/**
* Used to access an informal parameter that's a Block.
- *
+ *
* @param parameterName
- * the name of the informal parameter (case is ignored)
+ * the name of the informal parameter (case is ignored)
* @return the informal Block parameter, or null if not bound
*/
Block getBlockParameter(String parameterName);
/**
* Returns a previously stored render variable.
- *
+ *
* @param name
- * of the variable (case will be ignored)
+ * of the variable (case will be ignored)
* @return the variable's value
* @throws IllegalArgumentException
- * if the name doesn't correspond to a stored value
+ * if the name doesn't correspond to a stored value
*/
Object getRenderVariable(String name);
/**
* Stores a render variable, accessible with the provided name.
- *
+ *
* @param name
- * of value to store
+ * of value to store
* @param value
- * value to store (may not be null)
+ * value to store (may not be null)
* @throws IllegalStateException
- * if the component is not currently rendering
+ * if the component is not currently rendering
*/
void storeRenderVariable(String name, Object value);
/**
* Adds a listener object that will be notified about page lifecycle events.
+ *
+ * @deprecated In 5.3.3, use {@link #getPageLifecycleCallbackHub()} instead
*/
void addPageLifecycleListener(PageLifecycleListener listener);
/**
+ * Provides access to an object that can be used to register callbacks for page lifecycle events.
+ *
+ * @return the hub
+ * @since 5.3.3
+ */
+ PageLifecycleCallbackHub getPageLifecycleCallbackHub();
+
+ /**
* Removes a previously added listener.
- *
+ *
* @since 5.2.0
+ * @deprecated in 5.3.3, not necessary with {@link PageLifecycleCallbackHub#addPageLoadedCallback(Runnable)}.
*/
void removePageLifecycleListener(PageLifecycleListener listener);
@@ -190,25 +202,27 @@ public interface ComponentResources exte
/**
* Returns the name of element that represents the component in its template, or null if not known.
- *
+ *
* @return the element name or null
*/
String getElementName();
/**
* Returns a list of the names of any informal parameters bound to this component.
- *
+ *
* @return the name sorted alphabetically
* @see org.apache.tapestry5.annotations.SupportsInformalParameters
*/
List<String> getInformalParameterNames();
-/**
+ /**
* Reads an informal parameter and {@linkplain org.apache.tapestry5.ioc.services.TypeCoercer coercers} the bound
* value to the indicated type.
*
- * @param name name of informal parameter
- * @param type output value type
+ * @param name
+ * name of informal parameter
+ * @param type
+ * output value type
* @return instance of type
*/
<T> T getInformalParameter(String name, Class<T> type);
@@ -216,7 +230,7 @@ public interface ComponentResources exte
/**
* Returns true if these resources represent a mixin to another component. The component is the
* {@linkplain #getContainerResources() container} of this resources.
- *
+ *
* @since 5.2.0
*/
boolean isMixin();
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -519,16 +519,23 @@ public class ComponentPageElementImpl ex
* Constructor for other components embedded within the root component or at deeper levels of
* the hierarchy.
*
- * @param page ultimately containing this component
- * @param container component immediately containing this component (may be null for a root component)
- * @param id unique (within the container) id for this component (may be null for a root
- * component)
- * @param elementName the name of the element which represents this component in the template, or null
- * for
- * <comp> element or a page component
- * @param instantiator used to create the new component instance and access the component's model
- * @param location location of the element (within a template), used as part of exception reporting
- * @param elementResources Provides access to common methods of various services
+ * @param page
+ * ultimately containing this component
+ * @param container
+ * component immediately containing this component (may be null for a root component)
+ * @param id
+ * unique (within the container) id for this component (may be null for a root
+ * component)
+ * @param elementName
+ * the name of the element which represents this component in the template, or null
+ * for
+ * <comp> element or a page component
+ * @param instantiator
+ * used to create the new component instance and access the component's model
+ * @param location
+ * location of the element (within a template), used as part of exception reporting
+ * @param elementResources
+ * Provides access to common methods of various services
*/
ComponentPageElementImpl(Page page, ComponentPageElement container, String id, String nestedId, String completeId,
String elementName, Instantiator instantiator, Location location,
@@ -562,14 +569,11 @@ public class ComponentPageElementImpl ex
renderingValue = elementResources.createPerThreadValue();
- page.addLifecycleListener(new PageLifecycleAdapter()
+ page.addPageLoadedCallback(new Runnable()
{
- @Override
- public void containingPageDidLoad()
+ public void run()
{
pageLoaded();
-
- ComponentPageElementImpl.this.page.removeLifecycleListener(this);
}
});
}
@@ -783,7 +787,7 @@ public class ComponentPageElementImpl ex
initializeRenderPhases();
- page.addVerifyListener(new Runnable()
+ page.addVerifyCallback(new Runnable()
{
public void run()
{
@@ -940,10 +944,12 @@ public class ComponentPageElementImpl ex
/**
* Invokes a callback on the component instances (the core component plus any mixins).
*
- * @param reverse if true, the callbacks are in the reverse of the normal order (this is associated
- * with AfterXXX
- * phases)
- * @param callback the object to receive each component instance
+ * @param reverse
+ * if true, the callbacks are in the reverse of the normal order (this is associated
+ * with AfterXXX
+ * phases)
+ * @param callback
+ * the object to receive each component instance
*/
private void invoke(boolean reverse, ComponentCallback callback)
{
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import org.apache.tapestry5.ioc.internal
import org.apache.tapestry5.ioc.services.PerThreadValue;
import org.apache.tapestry5.model.ComponentModel;
import org.apache.tapestry5.runtime.Component;
-import org.apache.tapestry5.runtime.PageLifecycleAdapter;
+import org.apache.tapestry5.runtime.PageLifecycleCallbackHub;
import org.apache.tapestry5.runtime.PageLifecycleListener;
import org.apache.tapestry5.runtime.RenderQueue;
import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
@@ -42,6 +42,8 @@ import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* The bridge between a component and its {@link ComponentPageElement}, that supplies all kinds of
@@ -73,21 +75,35 @@ public class InternalComponentResourcesI
private static final AnnotationProvider NULL_ANNOTATION_PROVIDER = new NullAnnotationProvider();
- // Case insensitive map from parameter name to binding
+ // Map from parameter name to binding. This is mutable but not guarded by the lazy creation lock, as it is only
+ // written to during page load, not at runtime.
private NamedSet<Binding> bindings;
- // Case insensitive map from parameter name to ParameterConduit, used to support mixins
+ /**
+ * Lock, shared by all instances, on certain rare, runtime (not construction time) lazy creation, such
+ * as the creation of the {@link PerThreadValue}, to store render variables. It is possible that this lock could be converted
+ * into a static variable without affecting throughput since across all instances, reads vastly dominate writes.
+ */
+ private final ReadWriteLock lazyCreationLock = new ReentrantReadWriteLock();
+
+ // Maps from parameter name to ParameterConduit, used to support mixins
// which need access to the containing component's PC's
+ // Guarded by this
private NamedSet<ParameterConduit> conduits;
+ // Guarded by: lazyCreationLock
private Messages messages;
+ // Guarded by: lazyCreationLock
private boolean informalsComputed;
+ // Guarded by: lazyCreationLock
private PerThreadValue<Map<String, Object>> renderVariables;
+ // Guarded by: lazyCreationLock
private Informal firstInformal;
+
/**
* We keep a linked list of informal parameters, which saves us the expense of determining which
* bindings are formal
@@ -137,14 +153,6 @@ public class InternalComponentResourcesI
}
};
- private static Worker<ParameterConduit> LOAD_PARAMETER_CONDUIT = new Worker<ParameterConduit>()
- {
- public void work(ParameterConduit value)
- {
- value.load();
- }
- };
-
public InternalComponentResourcesImpl(Page page, ComponentPageElement element,
ComponentResources containerResources, ComponentPageElementResources elementResources, String completeId,
String nestedId, Instantiator componentInstantiator, boolean mixin)
@@ -375,19 +383,48 @@ public class InternalComponentResourcesI
i.write(writer);
}
- private synchronized Informal firstInformal()
+ private Informal firstInformal()
{
- if (!informalsComputed)
+ try
{
- for (Map.Entry<String, Binding> e : getInformalParameterBindings().entrySet())
+ lazyCreationLock.readLock().lock();
+
+ if (!informalsComputed)
{
- firstInformal = new Informal(e.getKey(), e.getValue(), firstInformal);
+ computeInformals();
}
- informalsComputed = true;
+ return firstInformal;
+ } finally
+ {
+ lazyCreationLock.readLock().unlock();
}
+ }
+
+ private void computeInformals()
+ {
+ try
+ {
+ lazyCreationLock.readLock().unlock();
+ // Tiny window here where some other thread may compute informals!
+ lazyCreationLock.writeLock().lock();
+
+ if (!informalsComputed)
+ {
+ for (Map.Entry<String, Binding> e : getInformalParameterBindings().entrySet())
+ {
+ firstInformal = new Informal(e.getKey(), e.getValue(), firstInformal);
+ }
- return firstInformal;
+ informalsComputed = true;
+ }
+ } finally
+ {
+ // Downgrade back to read:
+ lazyCreationLock.readLock().lock();
+ lazyCreationLock.writeLock().unlock();
+
+ }
}
public Component getContainer()
@@ -418,12 +455,51 @@ public class InternalComponentResourcesI
return element.getResourceSelector();
}
- public synchronized Messages getMessages()
+ public Messages getMessages()
{
- if (messages == null)
- messages = elementResources.getMessages(componentModel);
+ try
+ {
+ lazyCreationLock.readLock().lock();
+
+ if (messages == null)
+ {
+ obtainComponentMessages();
+ }
+
+ return messages;
+ } finally
+ {
+ lazyCreationLock.readLock().unlock();
+ }
+ }
+
+ private void obtainComponentMessages()
+ {
+
+ boolean haveWriteLock = false;
+
+ try
+ {
+ lazyCreationLock.readLock().unlock();
+
+ // Do the expensive part here, with no locks!
+
+ Messages componentMessages = elementResources.getMessages(componentModel);
- return messages;
+ lazyCreationLock.writeLock().lock();
+
+ haveWriteLock = true;
+
+ messages = componentMessages;
+ } finally
+ {
+ lazyCreationLock.readLock().lock();
+ if (haveWriteLock)
+ {
+ lazyCreationLock.writeLock().unlock();
+ }
+
+ }
}
public String getElementName(String defaultElementName)
@@ -471,22 +547,53 @@ public class InternalComponentResourcesI
return result;
}
- private synchronized Map<String, Object> getRenderVariables(boolean create)
+ private Map<String, Object> getRenderVariables(boolean create)
{
- if (renderVariables == null)
+ try
{
- if (!create)
- return null;
+ lazyCreationLock.readLock().lock();
+
+ if (renderVariables == null)
+ {
+ if (!create)
+ {
+ return null;
+ }
+
+ createRenderVariablesPerThreadValue();
+ }
- renderVariables = elementResources.createPerThreadValue();
+ Map<String, Object> result = renderVariables.get();
+
+ if (result == null && create)
+ result = renderVariables.set(CollectionFactory.newCaseInsensitiveMap());
+
+ return result;
+ } finally
+ {
+ lazyCreationLock.readLock().unlock();
}
+ }
- Map<String, Object> result = renderVariables.get();
+ private void createRenderVariablesPerThreadValue()
+ {
+ try
+ {
+ lazyCreationLock.readLock().unlock();
+ // There's a window right here where another thread may acquire the write lock and create the PTV
+ lazyCreationLock.writeLock().lock();
- if (result == null && create)
- result = renderVariables.set(CollectionFactory.newCaseInsensitiveMap());
+ if (renderVariables == null)
+ {
+ renderVariables = elementResources.createPerThreadValue();
+ }
- return result;
+ } finally
+ {
+ // The thread with the write lock is allowed to downgrade to a read lock as so:
+ lazyCreationLock.readLock().lock();
+ lazyCreationLock.writeLock().unlock();
+ }
}
public Object getRenderVariable(String name)
@@ -537,8 +644,6 @@ public class InternalComponentResourcesI
page.addResetListener(listener);
}
-
-
private synchronized void resetParameterConduits()
{
if (conduits != null)
@@ -547,15 +652,6 @@ public class InternalComponentResourcesI
}
}
- private synchronized void loadParameterConduits()
- {
- // Don't need the conduits != null, because this method will only be invoked
- // when conduits is non-null.
-
- conduits.eachValue(LOAD_PARAMETER_CONDUIT);
- }
-
-
public synchronized ParameterConduit getParameterConduit(String parameterName)
{
return NamedSet.get(conduits, parameterName);
@@ -566,16 +662,6 @@ public class InternalComponentResourcesI
if (conduits == null)
{
conduits = NamedSet.create();
-
- page.addLifecycleListener(new PageLifecycleAdapter()
- {
- @Override
- public void containingPageDidLoad()
- {
- loadParameterConduits();
- }
- });
-
}
conduits.put(parameterName, conduit);
@@ -599,9 +685,17 @@ public class InternalComponentResourcesI
return null;
}
- /** @since 5.3 */
+ /**
+ * @since 5.3
+ */
public void render(MarkupWriter writer, RenderQueue queue)
{
queue.push(element);
}
+
+ @Override
+ public PageLifecycleCallbackHub getPageLifecycleCallbackHub()
+ {
+ return page;
+ }
}
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.apache.tapestry5.internal.st
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.ioc.services.PerthreadManager;
import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.runtime.PageLifecycleCallbackHub;
import org.apache.tapestry5.runtime.PageLifecycleListener;
import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
import org.slf4j.Logger;
@@ -29,16 +30,16 @@ import org.slf4j.Logger;
* Starting in release 5.2, the nature of pages changed considerably. Pages are no longer pooled instances. Instead,
* pages are shared instances (per locale) but all internal <em>mutable</em> state is stored inside
* {@link PerthreadManager}. Page construction time is considered to extend past the
- * {@link PageLifecycleListener#containingPageDidLoad()} lifecycle notification. This is not quite perfect from a
+ * {@linkplain PageLifecycleCallbackHub#addPageLoadedCallback(Runnable) page loaded callback}. This is not quite perfect from a
* threading point-of-view (arguably, even write-once-read-many fields should be protected by synchronized blocks or
* other mechanisms). At best, we can be assured that the entire page construction phase is protected by a single
* synchronized block (but not on the page itself). An ideal system would build the page bottom to top so that all
* assignments could take place in constructors, assigning to final fields. Maybe some day.
* <p/>
- * The Page object is never visible to end-user code. The page also exists to provide a kind of service to components
- * embedded (directly or indirectly) within the page.
+ * The Page object is never visible to end-user code, though it exposes an interface ({@link PageLifecycleCallbackHub} that
+ * {@linkplain org.apache.tapestry5.ComponentResources#getPageLifecycleCallbackHub() is}).
*/
-public interface Page
+public interface Page extends PageLifecycleCallbackHub
{
/**
* Page construction statistics for the page.
@@ -141,6 +142,9 @@ public interface Page
/**
* Adds a listener that is notified of large scale page events.
+ *
+ * @deprecated in 5.3.3; use {@link #addPageLoadedCallback(Runnable)}, {@link #addPageAttachedCallback(Runnable)}, or
+ * {@link #addPageDetachedCallback(Runnable)} instead
*/
void addLifecycleListener(PageLifecycleListener listener);
@@ -148,6 +152,7 @@ public interface Page
* Removes a listener that was previously added.
*
* @since 5.2.0
+ * @deprecated in 5.3.3, due to introduction of {@link #addPageLoadedCallback(Runnable)}
*/
void removeLifecycleListener(PageLifecycleListener listener);
@@ -165,25 +170,31 @@ public interface Page
* string) returns the root
* element of the page.
*
- * @throws IllegalArgumentException if the nestedId does not correspond to a component
+ * @throws IllegalArgumentException
+ * if the nestedId does not correspond to a component
*/
ComponentPageElement getComponentElementByNestedId(String nestedId);
/**
* Posts a change to a persistent field.
*
- * @param resources the component resources for the component or mixin containing the field whose
- * value changed
- * @param fieldName the name of the field
- * @param newValue the new value for the field
+ * @param resources
+ * the component resources for the component or mixin containing the field whose
+ * value changed
+ * @param fieldName
+ * the name of the field
+ * @param newValue
+ * the new value for the field
*/
void persistFieldChange(ComponentResources resources, String fieldName, Object newValue);
/**
* Gets a change for a field within the component.
*
- * @param nestedId the nested component id of the component containing the field
- * @param fieldName the name of the persistent field
+ * @param nestedId
+ * the nested component id of the component containing the field
+ * @param fieldName
+ * the name of the persistent field
* @return the value, or null if no value is stored
*/
Object getFieldChange(String nestedId, String fieldName);
@@ -199,23 +210,14 @@ public interface Page
/**
* Adds a new listener for page reset events.
*
- * @param listener will receive notifications when the page is accessed from a different page
+ * @param listener
+ * will receive notifications when the page is accessed from a different page
* @since 5.2.0
+ * @deprecated in 5.3.3,
*/
void addResetListener(PageResetListener listener);
/**
- * Adds a verify callback, which is allowed while the page is loading. Such callbacks are invoked once,
- * after the page has been loaded succesfully. This was added specifically to ensure that components
- * only verify that required parameters are bound after all components and mixins of the page have had a chance
- * to initialize.
- *
- * @param callback to be invoked after page loaded
- * @since 5.3
- */
- void addVerifyListener(Runnable callback);
-
- /**
* Returns true if there are any {@link PageResetListener} listeners.
*
* @since 5.2.0
@@ -227,7 +229,6 @@ public interface Page
*/
void pageReset();
-
/**
* Invoked once at the end of page construction, to provide page construction statistics.
*
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.apache.tapestry5.internal.st
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.internal.services.PersistentFieldManager;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.internal.util.OneShotLock;
import org.apache.tapestry5.ioc.services.PerThreadValue;
import org.apache.tapestry5.ioc.services.PerthreadManager;
@@ -41,13 +42,19 @@ public class PageImpl implements Page
private ComponentPageElement rootElement;
- private final List<PageLifecycleListener> lifecycleListeners = CollectionFactory.newThreadSafeList();
+ private List<Runnable> loadedCallbacks = CollectionFactory.newList();
- private final List<PageResetListener> resetListeners = CollectionFactory.newList();
+ private final List<Runnable> attachCallbacks = CollectionFactory.newList();
+
+ private final List<Runnable> detachCallbacks = CollectionFactory.newList();
+
+ private final List<Runnable> resetCallbacks = CollectionFactory.newList();
private boolean loadComplete;
- private final OneShotLock lock = new OneShotLock();
+ private final OneShotLock lifecycleListenersLock = new OneShotLock();
+
+ private final OneShotLock verifyListenerLocks = new OneShotLock();
private final Map<String, ComponentPageElement> idToComponent = CollectionFactory.newCaseInsensitiveMap();
@@ -67,10 +74,14 @@ public class PageImpl implements Page
private static final Pattern SPLIT_ON_DOT = Pattern.compile("\\.");
/**
- * @param name canonicalized page name
- * @param selector used to locate resources
- * @param persistentFieldManager for access to cross-request persistent values
- * @param perThreadManager for managing per-request mutable state
+ * @param name
+ * canonicalized page name
+ * @param selector
+ * used to locate resources
+ * @param persistentFieldManager
+ * for access to cross-request persistent values
+ * @param perThreadManager
+ * for managing per-request mutable state
*/
public PageImpl(String name, ComponentResourceSelector selector, PersistentFieldManager persistentFieldManager,
PerthreadManager perThreadManager)
@@ -129,7 +140,7 @@ public class PageImpl implements Page
public void setRootElement(ComponentPageElement component)
{
- lock.check();
+ lifecycleListenersLock.check();
rootElement = component;
}
@@ -144,33 +155,59 @@ public class PageImpl implements Page
return rootElement.getComponent();
}
- public void addLifecycleListener(PageLifecycleListener listener)
+ public void addLifecycleListener(final PageLifecycleListener listener)
{
- lock.check();
+ assert listener != null;
- lifecycleListeners.add(listener);
+ addPageLoadedCallback(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ listener.containingPageDidLoad();
+ }
+ });
+
+ addPageAttachedCallback(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ listener.containingPageDidAttach();
+ }
+ });
+
+ addPageDetachedCallback(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ listener.containingPageDidDetach();
+ }
+ });
}
public void removeLifecycleListener(PageLifecycleListener listener)
{
- lock.check();
+ lifecycleListenersLock.check();
- lifecycleListeners.remove(listener);
+ throw new UnsupportedOperationException("It is not longer possible to remove a page lifecycle listener; please convert your code to use the addPageLoadedCallback() method instead.");
}
public boolean detached()
{
boolean result = false;
- for (PageLifecycleListener listener : lifecycleListeners)
+ for (Runnable callback : detachCallbacks)
{
try
{
- listener.containingPageDidDetach();
+ callback.run();
} catch (RuntimeException ex)
{
- getLogger().error(StructureMessages.detachFailure(listener, ex), ex);
result = true;
+
+ getLogger().error(String.format("Callback %s failed during page detach: %s", callback, InternalUtils.toMessage(ex)), ex);
}
}
@@ -179,25 +216,15 @@ public class PageImpl implements Page
public void loaded()
{
- lock.check();
-
- for (PageLifecycleListener listener : lifecycleListeners)
- {
- listener.containingPageDidLoad();
- }
+ lifecycleListenersLock.lock();
- lock.lock();
+ invokeCallbacks(loadedCallbacks);
+ loadedCallbacks = null;
- for (Runnable callback : pageVerifyCallbacks)
- {
- callback.run();
- }
+ verifyListenerLocks.lock();
- // These are never needed again, so we can get rid of them. The PageLifecycleListener interface is too complicated,
- // we don't know what's needed when, and rely on the listeners themselves to unregister when no longer needed. A better design
- // would be more like these pageVerifyCallbacks: just use Runnable and know when it is safe to throw them away. Something
- // to refactor to over time.
+ invokeCallbacks(pageVerifyCallbacks);
pageVerifyCallbacks = null;
@@ -208,11 +235,7 @@ public class PageImpl implements Page
{
attachCount.incrementAndGet();
- for (PageLifecycleListener listener : lifecycleListeners)
- listener.restoreStateBeforePageAttach();
-
- for (PageLifecycleListener listener : lifecycleListeners)
- listener.containingPageDidAttach();
+ invokeCallbacks(attachCallbacks);
}
public Logger getLogger()
@@ -246,38 +269,89 @@ public class PageImpl implements Page
return name;
}
- public void addResetListener(PageResetListener listener)
+ @Override
+ public void addResetCallback(Runnable callback)
+ {
+ assert callback != null;
+
+ lifecycleListenersLock.check();
+
+ resetCallbacks.add(callback);
+ }
+
+ public void addResetListener(final PageResetListener listener)
{
assert listener != null;
- lock.check();
- resetListeners.add(listener);
+ addResetCallback(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ listener.containingPageDidReset();
+ }
+ });
}
- public void addVerifyListener(Runnable callback)
+ public void addVerifyCallback(Runnable callback)
{
- assert callback != null;
+ verifyListenerLocks.check();
- lock.check();
+ assert callback != null;
pageVerifyCallbacks.add(callback);
}
public void pageReset()
{
- for (PageResetListener l : resetListeners)
- {
- l.containingPageDidReset();
- }
+ invokeCallbacks(resetCallbacks);
}
public boolean hasResetListeners()
{
- return !resetListeners.isEmpty();
+ return !resetCallbacks.isEmpty();
}
public int getAttachCount()
{
return attachCount.get();
}
+
+ @Override
+ public void addPageLoadedCallback(Runnable callback)
+ {
+ lifecycleListenersLock.check();
+
+ assert callback != null;
+
+ loadedCallbacks.add(callback);
+ }
+
+ @Override
+ public void addPageAttachedCallback(Runnable callback)
+ {
+ lifecycleListenersLock.check();
+
+ assert callback != null;
+
+ attachCallbacks.add(callback);
+ }
+
+ @Override
+ public void addPageDetachedCallback(Runnable callback)
+ {
+ lifecycleListenersLock.check();
+
+ assert callback != null;
+
+ detachCallbacks.add(callback);
+ }
+
+ private void invokeCallbacks(List<Runnable> callbacks)
+ {
+ for (Runnable callback : callbacks)
+ {
+ callback.run();
+ }
+ }
}
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import org.apache.tapestry5.annotations.
*
* @since 5.2.0
* @see PageReset
+ * @deprecated in 5.3.3
+ * @see org.apache.tapestry5.runtime.PageLifecycleCallbackHub#addResetCallback(Runnable)
*/
public interface PageResetListener
{
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/StructureMessages.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
package org.apache.tapestry5.internal.structure;
-import java.util.Collection;
-import java.util.List;
-
import org.apache.tapestry5.ioc.Location;
import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.internal.util.MessagesImpl;
+import java.util.Collection;
+import java.util.List;
+
final class StructureMessages
{
private static final Messages MESSAGES = MessagesImpl.forClass(StructureMessages.class);
@@ -41,11 +41,6 @@ final class StructureMessages
return MESSAGES.format("unknown-mixin", componentId, mixinClassName);
}
- static String detachFailure(Object listener, Throwable cause)
- {
- return MESSAGES.format("detach-failure", listener, cause);
- }
-
static String wrongPhaseResultType(List<String> expectedTypes)
{
return MESSAGES.format("wrong-phase-result-type", InternalUtils.join(expectedTypes));
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectComponentWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectComponentWorker.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectComponentWorker.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/InjectComponentWorker.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2008, 2010, 2011 The Apache Software Foundation
+// Copyright 2008, 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import org.apache.tapestry5.ioc.util.Unk
import org.apache.tapestry5.model.MutableComponentModel;
import org.apache.tapestry5.plastic.*;
import org.apache.tapestry5.runtime.Component;
-import org.apache.tapestry5.runtime.PageLifecycleAdapter;
import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
import org.apache.tapestry5.services.transform.TransformationSupport;
@@ -36,11 +35,12 @@ public class InjectComponentWorker imple
private final class InjectedComponentFieldValueConduit extends ReadOnlyComponentFieldConduit
{
private final ComponentResources resources;
+
private final String fieldName, componentId, type;
private Component embedded;
- private InjectedComponentFieldValueConduit(final ComponentResources resources, String fieldName, String type,
+ private InjectedComponentFieldValueConduit(ComponentResources resources, String fieldName, String type,
String componentId)
{
super(resources, fieldName);
@@ -50,13 +50,12 @@ public class InjectComponentWorker imple
this.componentId = componentId;
this.type = type;
- resources.addPageLifecycleListener(new PageLifecycleAdapter()
+ resources.getPageLifecycleCallbackHub().addPageAttachedCallback(new Runnable()
{
- public void containingPageDidLoad()
+ @Override
+ public void run()
{
load();
-
- resources.removePageLifecycleListener(this);
}
});
}
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterConduit.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2010, 2011 The Apache Software Foundation
+// Copyright 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -45,11 +45,4 @@ public interface ParameterConduit extend
* Resets the conduit, clearing any <em>temporarily</em> cached data (from a non-invariant {@link Binding}).
*/
void reset();
-
- /**
- * Invoked from the component's {@link org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidLoad()} lifecycle method, to
- * finishing initializing
- * the conduit prior to real use.
- */
- void load();
}
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java Sat May 12 06:53:16 2012
@@ -114,6 +114,7 @@ public class ParameterWorker implements
private void convertFieldIntoParameter(PlasticClass plasticClass, MutableComponentModel model,
PlasticField field)
{
+
Parameter annotation = field.getAnnotation(Parameter.class);
String fieldType = field.getTypeName();
@@ -195,6 +196,14 @@ public class ParameterWorker implements
// shared with mixins.
icr.setParameterConduit(parameterName, this);
+ icr.getPageLifecycleCallbackHub().addPageLoadedCallback(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ load();
+ }
+ });
}
private ParameterState getState()
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,12 +15,15 @@
package org.apache.tapestry5.internal.util;
import org.apache.commons.collections.map.CaseInsensitiveMap;
+import org.apache.tapestry5.func.F;
import org.apache.tapestry5.func.Worker;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import java.util.Collections;
import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Simple, thread-safe associative array that relates a name to a value. Names are case-insensitive.
@@ -28,13 +31,15 @@ import java.util.Set;
* though the cost of a lookup is more expensive. However, this is a good match against many of the structures inside
* a page instance, where most lookups occur only during page constructions, and the number of values is often small.
* <p/>
- * We use simple synchronization, as uncontested synchronized locks are very, very cheap.
+ * Each NameSet has its own {@link ReadWriteLock}.
*
* @param <T>
* the type of value stored
*/
public class NamedSet<T>
{
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
private NamedRef<T> first;
private static class NamedRef<T>
@@ -55,37 +60,53 @@ public class NamedSet<T>
/**
* Returns a set of the names of all stored values.
*/
- public synchronized Set<String> getNames()
+ public Set<String> getNames()
{
- Set<String> result = CollectionFactory.newSet();
+ try
+ {
+ lock.readLock().lock();
+
+ Set<String> result = CollectionFactory.newSet();
- NamedRef<T> cursor = first;
+ NamedRef<T> cursor = first;
+
+ while (cursor != null)
+ {
+ result.add(cursor.name);
+ cursor = cursor.next;
+ }
- while (cursor != null)
+ return result;
+ } finally
{
- result.add(cursor.name);
- cursor = cursor.next;
+ lock.readLock().unlock();
}
-
- return result;
}
/**
* Returns a set of all the values in the set.
*/
- public synchronized Set<T> getValues()
+ public Set<T> getValues()
{
Set<T> result = CollectionFactory.newSet();
- NamedRef<T> cursor = first;
+ try
+ {
+ lock.readLock().lock();
+
+ NamedRef<T> cursor = first;
+
+ while (cursor != null)
+ {
+ result.add(cursor.value);
+ cursor = cursor.next;
+ }
- while (cursor != null)
+ return result;
+ } finally
{
- result.add(cursor.value);
- cursor = cursor.next;
+ lock.readLock().unlock();
}
-
- return result;
}
/**
@@ -95,21 +116,29 @@ public class NamedSet<T>
* used to locate the value
* @return the value, or null if not found
*/
- public synchronized T get(String name)
+ public T get(String name)
{
- NamedRef<T> cursor = first;
-
- while (cursor != null)
+ try
{
- if (cursor.name.equalsIgnoreCase(name))
+ lock.readLock().lock();
+
+ NamedRef<T> cursor = first;
+
+ while (cursor != null)
{
- return cursor.value;
+ if (cursor.name.equalsIgnoreCase(name))
+ {
+ return cursor.value;
+ }
+
+ cursor = cursor.next;
}
- cursor = cursor.next;
+ return null;
+ } finally
+ {
+ lock.readLock().unlock();
}
-
- return null;
}
/**
@@ -121,37 +150,45 @@ public class NamedSet<T>
* @param newValue
* non-null value to store
*/
- public synchronized void put(String name, T newValue)
+ public void put(String name, T newValue)
{
assert InternalUtils.isNonBlank(name);
assert newValue != null;
- NamedRef<T> prev = null;
- NamedRef<T> cursor = first;
-
- while (cursor != null)
+ try
{
- if (cursor.name.equalsIgnoreCase(name))
+ lock.writeLock().lock();
+
+ NamedRef<T> prev = null;
+ NamedRef<T> cursor = first;
+
+ while (cursor != null)
{
- // Retain the case of the name as put(), even if it doesn't match
- // the existing case
+ if (cursor.name.equalsIgnoreCase(name))
+ {
+ // Retain the case of the name as put(), even if it doesn't match
+ // the existing case
- cursor.name = name;
- cursor.value = newValue;
+ cursor.name = name;
+ cursor.value = newValue;
- return;
- }
+ return;
+ }
- prev = cursor;
- cursor = cursor.next;
- }
+ prev = cursor;
+ cursor = cursor.next;
+ }
- NamedRef<T> newRef = new NamedRef<T>(name, newValue);
+ NamedRef<T> newRef = new NamedRef<T>(name, newValue);
- if (prev == null)
- first = newRef;
- else
- prev.next = newRef;
+ if (prev == null)
+ first = newRef;
+ else
+ prev.next = newRef;
+ } finally
+ {
+ lock.writeLock().unlock();
+ }
}
/**
@@ -160,17 +197,9 @@ public class NamedSet<T>
* @param worker
* performs an operation on, or using, the value
*/
- public synchronized void eachValue(Worker<T> worker)
+ public void eachValue(Worker<T> worker)
{
- assert worker != null;
-
- NamedRef<T> cursor = first;
-
- while (cursor != null)
- {
- worker.work(cursor.value);
- cursor = cursor.next;
- }
+ F.flow(getValues()).each(worker);
}
@@ -183,37 +212,46 @@ public class NamedSet<T>
* non-null value to store
* @return true if value stored, false if name already exists
*/
- public synchronized boolean putIfNew(String name, T newValue)
+ public boolean putIfNew(String name, T newValue)
{
assert InternalUtils.isNonBlank(name);
assert newValue != null;
- NamedRef<T> prev = null;
- NamedRef<T> cursor = first;
-
- while (cursor != null)
+ try
{
- if (cursor.name.equalsIgnoreCase(name))
+
+ lock.writeLock().lock();
+
+ NamedRef<T> prev = null;
+ NamedRef<T> cursor = first;
+
+ while (cursor != null)
{
- return false;
- }
+ if (cursor.name.equalsIgnoreCase(name))
+ {
+ return false;
+ }
- prev = cursor;
- cursor = cursor.next;
- }
+ prev = cursor;
+ cursor = cursor.next;
+ }
- NamedRef<T> newRef = new NamedRef<T>(name, newValue);
+ NamedRef<T> newRef = new NamedRef<T>(name, newValue);
- if (prev == null)
- first = newRef;
- else
- prev.next = newRef;
+ if (prev == null)
+ first = newRef;
+ else
+ prev.next = newRef;
- return true;
+ return true;
+ } finally
+ {
+ lock.writeLock().unlock();
+ }
}
/**
- * Convienience method for creating a new, empty set.
+ * Convenience method for creating a new, empty set.
*/
public static <T> NamedSet<T> create()
{
@@ -221,7 +259,7 @@ public class NamedSet<T>
}
/**
- * Convienience method for getting a value from a set that may be null.
+ * Convenience method for getting a value from a set that may be null.
*
* @param <T>
* @param set
@@ -241,7 +279,9 @@ public class NamedSet<T>
public static Set<String> getNames(NamedSet<?> set)
{
if (set == null)
+ {
return Collections.emptySet();
+ }
return set.getNames();
}
@@ -252,7 +292,9 @@ public class NamedSet<T>
public static <T> Set<T> getValues(NamedSet<T> set)
{
if (set == null)
+ {
return Collections.emptySet();
+ }
return set.getValues();
}
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@ package org.apache.tapestry5.runtime;
/**
* Empty implementation of the {@link PageLifecycleListener} interface.
+ *
+ * @deprecated in 5.3.3, as {@link PageLifecycleListener} has been deprecated
*/
public class PageLifecycleAdapter implements PageLifecycleListener
{
Added: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java?rev=1337454&view=auto
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java (added)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java Sat May 12 06:53:16 2012
@@ -0,0 +1,70 @@
+// Copyright 2012 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.runtime;
+
+/**
+ * Defines a way for different aspects of a page to add callbacks for important lifecycle events.
+ *
+ * @see org.apache.tapestry5.ComponentResources#getPageLifecycleCallbackHub()
+ * @since 5.3.3
+ */
+public interface PageLifecycleCallbackHub
+{
+ /**
+ * Adds a callback for when the page is first loaded. Callbacks are invoked in the order they
+ * are added to the page. They are invoked once and then discarded.
+ *
+ * @param callback
+ * invoked once, when page is first loaded
+ * @since 5.3.3
+ */
+ void addPageLoadedCallback(Runnable callback);
+
+ /**
+ * Adds a callback for when the page is attached to the request.
+ *
+ * @param callback
+ * @since 5.3.3
+ */
+ void addPageAttachedCallback(Runnable callback);
+
+ /**
+ * Adds a callback for when the page is detached from the request.
+ *
+ * @param callback
+ * @since 5.3.3
+ */
+ void addPageDetachedCallback(Runnable callback);
+
+ /**
+ * Adds a verify callback, which is allowed while the page is loading. Such callbacks are invoked once,
+ * after the page has been loaded successfully, and are then discarded. This was added specifically to ensure that components
+ * only verify that required parameters are bound after all components and mixins of the page have had a chance
+ * to initialize.
+ *
+ * @param callback
+ * to be invoked after page loaded
+ * @since 5.3
+ */
+ void addVerifyCallback(Runnable callback);
+
+ /**
+ * A reset occurs when a request for a page arrives that did not originate on the same page. This gives the application a chance to reset the state of the page.
+ *
+ * @param callback
+ * invoked when a page is activated by link from some other page.
+ */
+ void addResetCallback(Runnable callback);
+}
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2006, 2009, 2011 The Apache Software Foundation
+// Copyright 2006, 2009, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,12 +18,16 @@ package org.apache.tapestry5.runtime;
* A set of methods that allow components to know about page-level operations. When this interface is
* {@linkplain org.apache.tapestry5.plastic.PlasticClass#introduceInterface(Class)} introduced}, the component will
* automatically register itself as a listener with the page.
+ *
+ * @deprecated in 5.3.3, replaced with {@link PageLifecycleCallbackHub}
*/
public interface PageLifecycleListener
{
/**
* Invoked when the page finishes loading. This occurs once all components are loaded and all parameters have been
* set.
+ *
+ * @deprecated in 5.3.3, use {@link org.apache.tapestry5.ComponentResources#addPageLoadedCallback(Runnable)} instead
*/
void containingPageDidLoad();
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/main/resources/org/apache/tapestry5/internal/structure/StructureStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/main/resources/org/apache/tapestry5/internal/structure/StructureStrings.properties?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/main/resources/org/apache/tapestry5/internal/structure/StructureStrings.properties (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/main/resources/org/apache/tapestry5/internal/structure/StructureStrings.properties Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-# Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+# Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
missing-parameters=Parameter(s) '%s' are required for %s, but have not been bound.
unknown-mixin=Component %s does not contain a mixin of type %s.
-detach-failure=Listener %s failed during page detach: %s
wrong-phase-result-type=The return value from a render phase event method was not compatible with the expected return type. Expected is a component, a block or an instance of %s. \
You should change the method to return the correct type.
block-not-found=Template for component %s does not contain a block with identifier '%s'.
Modified: tapestry/tapestry5/branches/5.3/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/5.3/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java?rev=1337454&r1=1337453&r2=1337454&view=diff
==============================================================================
--- tapestry/tapestry5/branches/5.3/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java (original)
+++ tapestry/tapestry5/branches/5.3/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java Sat May 12 06:53:16 2012
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,9 +18,6 @@ import org.apache.tapestry5.internal.tes
import org.apache.tapestry5.ioc.services.PerthreadManager;
import org.apache.tapestry5.runtime.PageLifecycleListener;
import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
-
-import static org.easymock.EasyMock.contains;
-import static org.easymock.EasyMock.same;
import org.slf4j.Logger;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
@@ -28,6 +25,9 @@ import org.testng.annotations.Test;
import java.util.Locale;
+import static org.easymock.EasyMock.contains;
+import static org.easymock.EasyMock.same;
+
public class PageImplTest extends InternalBaseTestCase
{
private final ComponentResourceSelector selector = new ComponentResourceSelector(Locale.ENGLISH);
@@ -135,9 +135,6 @@ public class PageImplTest extends Intern
PageLifecycleListener listener1 = newPageLifecycle();
PageLifecycleListener listener2 = newPageLifecycle();
- listener1.restoreStateBeforePageAttach();
- listener2.restoreStateBeforePageAttach();
-
listener1.containingPageDidAttach();
listener2.containingPageDidAttach();