You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2010/06/25 02:05:37 UTC

svn commit: r957755 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/internal/ main/java/org/apache/tapestry5/internal/services/ main/java/org/apache/tapestry5/services/ test/java/org/apache/tapestry5/internal/services/

Author: hlship
Date: Fri Jun 25 00:05:37 2010
New Revision: 957755

URL: http://svn.apache.org/viewvc?rev=957755&view=rev
Log:
TAP5-1190: Add new LinkCreationListener2 interface, modify LinkCreationHub and LinkSource to make use of it
Deprecate LinkCreationListener
Change LinkSource to take a ordered configuration of LinkCreationListener2

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener2.java
      - copied, changed from r957377, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSource.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationHub.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/LinkSourceImplTest.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java?rev=957755&r1=957754&r2=957755&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java Fri Jun 25 00:05:37 2010
@@ -25,6 +25,7 @@ import java.util.regex.Pattern;
 import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.Asset2;
 import org.apache.tapestry5.EventContext;
+import org.apache.tapestry5.Link;
 import org.apache.tapestry5.OptionModel;
 import org.apache.tapestry5.PropertyConduit;
 import org.apache.tapestry5.SelectModel;
@@ -36,6 +37,10 @@ import org.apache.tapestry5.ioc.Resource
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.Defense;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.services.ComponentEventRequestParameters;
+import org.apache.tapestry5.services.LinkCreationListener;
+import org.apache.tapestry5.services.LinkCreationListener2;
+import org.apache.tapestry5.services.PageRenderRequestParameters;
 import org.apache.tapestry5.services.javascript.StylesheetLink;
 
 /**
@@ -572,4 +577,21 @@ public class TapestryInternalUtils
             return new StylesheetLink(input);
         };
     };
+
+    public static LinkCreationListener2 toLinkCreationListener2(final LinkCreationListener delegate)
+    {
+        return new LinkCreationListener2()
+        {
+
+            public void createdPageRenderLink(Link link, PageRenderRequestParameters parameters)
+            {
+                delegate.createdPageRenderLink(link);
+            }
+
+            public void createdComponentEventLink(Link link, ComponentEventRequestParameters parameters)
+            {
+                delegate.createdComponentEventLink(link);
+            }
+        };
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSource.java?rev=957755&r1=957754&r2=957755&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSource.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSource.java Fri Jun 25 00:05:37 2010
@@ -1,10 +1,10 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2009, 2010 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
+// 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,
@@ -16,26 +16,32 @@ package org.apache.tapestry5.internal.se
 
 import org.apache.tapestry5.Link;
 import org.apache.tapestry5.internal.structure.Page;
+import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
 import org.apache.tapestry5.services.LinkCreationHub;
+import org.apache.tapestry5.services.LinkCreationListener2;
 
 /**
  * A source for {@link org.apache.tapestry5.Link} objects.
- *
+ * 
  * @see org.apache.tapestry5.services.LinkCreationListener
  * @since 5.1.0.0
  */
+@UsesOrderedConfiguration(LinkCreationListener2.class)
 public interface LinkSource
 {
     /**
      * Creates a stateful action link. Action links are built for components. Action links are encoded by the current
      * request (that is, bound to the current request's session, if any).
-     *
+     * 
      * @param page
      * @param nestedId
-     * @param eventType the type of event to trigger
-     * @param forForm   true if the link is for a form, false otherwise
-     * @param context   Additional path data, each value will be converted to a string and appended to the URI @return a
-     *                  link
+     * @param eventType
+     *            the type of event to trigger
+     * @param forForm
+     *            true if the link is for a form, false otherwise
+     * @param context
+     *            Additional path data, each value will be converted to a string and appended to the URI @return a
+     *            link
      * @see org.apache.tapestry5.ComponentResources#createActionLink(String, boolean, Object[])
      */
     Link createComponentEventLink(Page page, String nestedId, String eventType, boolean forForm, Object... context);
@@ -45,13 +51,16 @@ public interface LinkSource
      * If no activation context is supplied, then the activation context is obtained from the page itself, by triggering
      * a passivate event on its root component.
      * <p/>
-     * When the activationContext is an empty array, the targetted page is checked to see if it can provide an
-     * activation context. This is accomplished by triggering a "passivate" event on the targetted page. If the override
-     * parameter is true, this will not occur (even when the activation context is empty).
-     *
-     * @param pageName              name of the page to which a link should be created
-     * @param override              if true, then the provided activation context is always used even if empty
-     * @param pageActivationContext the activation context for the page
+     * When the activationContext is an empty array, the targeted page is checked to see if it can provide an activation
+     * context. This is accomplished by triggering a "passivate" event on the targeted page. If the override parameter
+     * is true, this will not occur (even when the activation context is empty).
+     * 
+     * @param pageName
+     *            name of the page to which a link should be created
+     * @param override
+     *            if true, then the provided activation context is always used even if empty
+     * @param pageActivationContext
+     *            the activation context for the page
      * @return a link
      * @see org.apache.tapestry5.ComponentResources#createPageLink(String, boolean, Object[])
      */
@@ -59,7 +68,7 @@ public interface LinkSource
 
     /**
      * Returns the hub, used to register and de-register {@link org.apache.tapestry5.services.LinkCreationListener}s.
-     *
+     * 
      * @return the hub
      */
     LinkCreationHub getLinkCreationHub();

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java?rev=957755&r1=957754&r2=957755&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java Fri Jun 25 00:05:37 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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,6 +15,7 @@
 package org.apache.tapestry5.internal.services;
 
 import org.apache.tapestry5.Link;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
 import org.apache.tapestry5.internal.structure.Page;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.Defense;
@@ -31,7 +32,7 @@ public class LinkSourceImpl implements L
 
     private final ComponentEventLinkEncoder linkEncoder;
 
-    private final List<LinkCreationListener> listeners = CollectionFactory.newThreadSafeList();
+    private final List<LinkCreationListener2> listeners = CollectionFactory.newThreadSafeList();
 
     private final TypeCoercer typeCoercer;
 
@@ -41,10 +42,9 @@ public class LinkSourceImpl implements L
 
     private final RequestPageCache pageCache;
 
-    public LinkSourceImpl(PageRenderQueue pageRenderQueue,
-            PageActivationContextCollector contextCollector, TypeCoercer typeCoercer,
-            ComponentClassResolver resolver, ComponentEventLinkEncoder linkEncoder,
-            RequestGlobals requestGlobals, RequestPageCache pageCache)
+    public LinkSourceImpl(PageRenderQueue pageRenderQueue, PageActivationContextCollector contextCollector,
+            TypeCoercer typeCoercer, ComponentClassResolver resolver, ComponentEventLinkEncoder linkEncoder,
+            RequestGlobals requestGlobals, RequestPageCache pageCache, List<LinkCreationListener2> configuration)
     {
         this.pageRenderQueue = pageRenderQueue;
         this.contextCollector = contextCollector;
@@ -53,10 +53,12 @@ public class LinkSourceImpl implements L
         this.linkEncoder = linkEncoder;
         this.requestGlobals = requestGlobals;
         this.pageCache = pageCache;
+
+        listeners.addAll(configuration);
     }
 
-    public Link createComponentEventLink(Page page, String nestedId, String eventType,
-            boolean forForm, Object... eventContext)
+    public Link createComponentEventLink(Page page, String nestedId, String eventType, boolean forForm,
+            Object... eventContext)
     {
         Defense.notNull(page, "page");
         Defense.notBlank(eventType, "action");
@@ -69,18 +71,17 @@ public class LinkSourceImpl implements L
 
         String activePageName = activePage.getName();
 
-        Object[] pageActivationContext = contextCollector
-                .collectPageActivationContext(activePageName);
+        Object[] pageActivationContext = contextCollector.collectPageActivationContext(activePageName);
 
-        ComponentEventRequestParameters parameters = new ComponentEventRequestParameters(
-                activePageName, page.getName(), toBlank(nestedId), eventType,
-                new ArrayEventContext(typeCoercer, pageActivationContext), new ArrayEventContext(
-                        typeCoercer, eventContext));
+        ComponentEventRequestParameters parameters = new ComponentEventRequestParameters(activePageName,
+                page.getName(), toBlank(nestedId), eventType,
+                new ArrayEventContext(typeCoercer, pageActivationContext), new ArrayEventContext(typeCoercer,
+                        eventContext));
 
         Link link = linkEncoder.createComponentEventLink(parameters, forForm);
 
-        for (LinkCreationListener listener : listeners)
-            listener.createdComponentEventLink(link);
+        for (LinkCreationListener2 listener : listeners)
+            listener.createdComponentEventLink(link, parameters);
 
         return link;
     }
@@ -90,8 +91,7 @@ public class LinkSourceImpl implements L
         return input == null ? "" : input;
     }
 
-    public Link createPageRenderLink(String pageName, boolean override,
-            Object... pageActivationContext)
+    public Link createPageRenderLink(String pageName, boolean override, Object... pageActivationContext)
     {
         // Resolve the page name to its canonical format (the best version for URLs). This also
         // validates
@@ -99,19 +99,19 @@ public class LinkSourceImpl implements L
 
         String canonical = resolver.canonicalizePageName(pageName);
 
-        Object[] context = (override || pageActivationContext.length != 0) ? pageActivationContext
-                : contextCollector.collectPageActivationContext(canonical);
+        Object[] context = (override || pageActivationContext.length != 0) ? pageActivationContext : contextCollector
+                .collectPageActivationContext(canonical);
 
         boolean loopback = canonical.equals(requestGlobals.getActivePageName())
                 && pageCache.get(pageName).hasResetListeners();
 
-        PageRenderRequestParameters parameters = new PageRenderRequestParameters(canonical,
-                new ArrayEventContext(typeCoercer, context), loopback);
+        PageRenderRequestParameters parameters = new PageRenderRequestParameters(canonical, new ArrayEventContext(
+                typeCoercer, context), loopback);
 
         Link link = linkEncoder.createPageRenderLink(parameters);
 
-        for (LinkCreationListener listener : listeners)
-            listener.createdPageRenderLink(link);
+        for (LinkCreationListener2 listener : listeners)
+            listener.createdPageRenderLink(link, parameters);
 
         return link;
     }
@@ -125,13 +125,18 @@ public class LinkSourceImpl implements L
     {
         Defense.notNull(listener, "listener");
 
-        listeners.add(listener);
+        addListener(TapestryInternalUtils.toLinkCreationListener2(listener));
     }
 
     public void removeListener(LinkCreationListener listener)
     {
+        throw new UnsupportedOperationException("Removing listeners from LinkSource is not longer supported.");
+    }
+
+    public void addListener(LinkCreationListener2 listener)
+    {
         Defense.notNull(listener, "listener");
 
-        listeners.remove(listener);
+        listeners.add(listener);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationHub.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationHub.java?rev=957755&r1=957754&r2=957755&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationHub.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationHub.java Fri Jun 25 00:05:37 2010
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2010 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,7 +15,7 @@
 package org.apache.tapestry5.services;
 
 /**
- * A service that allows listeners to be registerred to learn about {@link org.apache.tapestry5.Link} creation.
+ * A service that allows listeners to be registered to learn about {@link org.apache.tapestry5.Link} creation.
  */
 public interface LinkCreationHub
 {
@@ -32,4 +32,11 @@ public interface LinkCreationHub
      * @param listener
      */
     void removeListener(LinkCreationListener listener);
+    
+    /**
+     * Adds a listener. If the scope of the listener is per-thread, then it must be removed.
+     *
+     * @param listener
+     */
+    void addListener(LinkCreationListener2 listener); 
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java?rev=957755&r1=957754&r2=957755&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java Fri Jun 25 00:05:37 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2010 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
+// 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,
@@ -18,24 +18,27 @@ import org.apache.tapestry5.Link;
 
 /**
  * Listener interface for objects that need to be notified about newly created links.
- *
+ * 
  * @see org.apache.tapestry5.services.LinkCreationHub
+ * @deprecated Use {@link LinkCreationListener2} instead
  */
 public interface LinkCreationListener
 {
     /**
      * Invoked when a page link (a link that renders a page) is created. The listener may decide to encode additional
      * query parameters into the link (via {@link Link#addParameter(String, String)}).
-     *
-     * @param link the newly created link
+     * 
+     * @param link
+     *            the newly created link
      */
     void createdPageRenderLink(Link link);
 
     /**
      * Invoked when an action link (a link that asks a component to perform an action) is created. The listener may
      * decide to encode additional query parameters into the link (via {@link Link#addParameter(String, String)}).
-     *
-     * @param link the newly created link
+     * 
+     * @param link
+     *            the newly created link
      */
     void createdComponentEventLink(Link link);
 }

Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener2.java (from r957377, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener2.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener2.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java&r1=957377&r2=957755&rev=957755&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/LinkCreationListener2.java Fri Jun 25 00:05:37 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2010 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
+// 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,
@@ -16,26 +16,28 @@ package org.apache.tapestry5.services;
 
 import org.apache.tapestry5.Link;
 
-/**
- * Listener interface for objects that need to be notified about newly created links.
- *
- * @see org.apache.tapestry5.services.LinkCreationHub
- */
-public interface LinkCreationListener
+public interface LinkCreationListener2
 {
     /**
      * Invoked when a page link (a link that renders a page) is created. The listener may decide to encode additional
      * query parameters into the link (via {@link Link#addParameter(String, String)}).
-     *
-     * @param link the newly created link
+     * 
+     * @param link
+     *            the newly created link
+     * @param parameters
+     *            information encoded into the link
      */
-    void createdPageRenderLink(Link link);
+
+    void createdPageRenderLink(Link link, PageRenderRequestParameters parameters);
 
     /**
      * Invoked when an action link (a link that asks a component to perform an action) is created. The listener may
      * decide to encode additional query parameters into the link (via {@link Link#addParameter(String, String)}).
-     *
-     * @param link the newly created link
+     * 
+     * @param link
+     *            the newly created link
+     * @param parameters
+     *            information encoded into the link
      */
-    void createdComponentEventLink(Link link);
+    void createdComponentEventLink(Link link, ComponentEventRequestParameters parameters);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/LinkSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/LinkSourceImplTest.java?rev=957755&r1=957754&r2=957755&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/LinkSourceImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/LinkSourceImplTest.java Fri Jun 25 00:05:37 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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,11 +14,15 @@
 
 package org.apache.tapestry5.internal.services;
 
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.tapestry5.EventConstants;
 import org.apache.tapestry5.EventContext;
 import org.apache.tapestry5.Link;
 import org.apache.tapestry5.internal.structure.Page;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.services.*;
 import org.testng.annotations.BeforeClass;
@@ -26,6 +30,8 @@ import org.testng.annotations.Test;
 
 public class LinkSourceImplTest extends InternalBaseTestCase
 {
+    private List<LinkCreationListener2> emptyConfiguration = Collections.emptyList();
+
     private TypeCoercer typeCoercer;
 
     @BeforeClass
@@ -73,7 +79,7 @@ public class LinkSourceImplTest extends 
     @Test
     public void create_page_render_link_with_event_context_from_passivate()
     {
-        testPageLinkCreation("order/Edit", false, "from", "passivate");
+        testPageLinkCreation2("order/Edit", false, "from", "passivate");
     }
 
     @Test
@@ -94,15 +100,14 @@ public class LinkSourceImplTest extends 
         train_collectPageActivationContext(collector, pageName, 3);
 
         EventContext pageActivationContext = new ArrayEventContext(typeCoercer, 3);
-        PageRenderRequestParameters parameters = new PageRenderRequestParameters(pageName,
-                pageActivationContext);
+        PageRenderRequestParameters parameters = new PageRenderRequestParameters(pageName, pageActivationContext);
 
         expect(linkEncoder.createPageRenderLink(parameters)).andReturn(link);
 
         replay();
 
-        LinkSource source = new LinkSourceImpl(null, collector, typeCoercer, resolver, linkEncoder,
-                globals, null);
+        LinkSource source = new LinkSourceImpl(null, collector, typeCoercer, resolver, linkEncoder, globals, null,
+                emptyConfiguration);
 
         Link actual = source.createPageRenderLink(pageName, false);
 
@@ -133,8 +138,8 @@ public class LinkSourceImplTest extends 
         if (!overrideContext)
             train_collectPageActivationContext(collector, canonical, context);
 
-        PageRenderRequestParameters parameters = new PageRenderRequestParameters(canonical,
-                new ArrayEventContext(typeCoercer, context), false);
+        PageRenderRequestParameters parameters = new PageRenderRequestParameters(canonical, new ArrayEventContext(
+                typeCoercer, context), false);
 
         expect(linkEncoder.createPageRenderLink(parameters)).andReturn(link);
 
@@ -142,8 +147,51 @@ public class LinkSourceImplTest extends 
 
         replay();
 
-        LinkSource source = new LinkSourceImpl(null, collector, typeCoercer, resolver, linkEncoder,
-                globals, null);
+        LinkSource source = new LinkSourceImpl(null, collector, typeCoercer, resolver, linkEncoder, globals, null,
+                emptyConfiguration);
+
+        source.getLinkCreationHub().addListener(listener);
+
+        Object[] passedContext = overrideContext ? context : new Object[0];
+
+        Link returnedLink = source.createPageRenderLink(pageName, overrideContext, passedContext);
+
+        assertSame(returnedLink, link);
+
+        verify();
+    }
+
+    private void testPageLinkCreation2(String pageName, boolean overrideContext, Object... context)
+    {
+        PageActivationContextCollector collector = mockPageActivationContextCollector();
+        LinkCreationListener2 listener = mockLinkCreationListener2();
+        ComponentEventLinkEncoder linkEncoder = mockComponentEventLinkEncoder();
+        Link link = mockLink();
+        ComponentClassResolver resolver = mockComponentClassResolver();
+        String canonical = "CanonicalPageName";
+        RequestGlobals globals = mockRequestGlobals();
+
+        // Ensure that the loopback logic is off; loopback logic is fully tested via an
+        // integration test.
+
+        expect(globals.getActivePageName()).andReturn("someOtherPage");
+
+        train_canonicalizePageName(resolver, pageName, canonical);
+
+        if (!overrideContext)
+            train_collectPageActivationContext(collector, canonical, context);
+
+        PageRenderRequestParameters parameters = new PageRenderRequestParameters(canonical, new ArrayEventContext(
+                typeCoercer, context), false);
+
+        expect(linkEncoder.createPageRenderLink(parameters)).andReturn(link);
+
+        listener.createdPageRenderLink(link, parameters);
+
+        replay();
+
+        LinkSource source = new LinkSourceImpl(null, collector, typeCoercer, resolver, linkEncoder, globals, null,
+                emptyConfiguration);
 
         source.getLinkCreationHub().addListener(listener);
 
@@ -156,6 +204,11 @@ public class LinkSourceImplTest extends 
         verify();
     }
 
+    private LinkCreationListener2 mockLinkCreationListener2()
+    {
+        return newMock(LinkCreationListener2.class);
+    }
+
     protected final ComponentEventLinkEncoder mockComponentEventLinkEncoder()
     {
         return newMock(ComponentEventLinkEncoder.class);
@@ -171,15 +224,13 @@ public class LinkSourceImplTest extends 
     @Test
     public void component_event_link_with_context()
     {
-        testEventLinkCreation("order/Edit", "foo.bar", EventConstants.ACTION, false, "fred",
-                "barney");
+        testEventLinkCreation("order/Edit", "foo.bar", EventConstants.ACTION, false, "fred", "barney");
     }
 
     @Test
     public void component_event_link_for_form()
     {
-        testEventLinkCreation("order/Edit", "foo.bar", EventConstants.ACTION, true, "fred",
-                "barney");
+        testEventLinkCreation2("order/Edit", "foo.bar", EventConstants.ACTION, true, "fred", "barney");
     }
 
     @Test
@@ -204,25 +255,23 @@ public class LinkSourceImplTest extends 
         EventContext pageActivationContext = new ArrayEventContext(typeCoercer, "x", "y");
         EventContext eventContext = new ArrayEventContext(typeCoercer, 3, 5, 9);
 
-        ComponentEventRequestParameters parameters = new ComponentEventRequestParameters(
-                "order/View", primaryPageName, "gnip.gnop", "myevent", pageActivationContext,
-                eventContext);
+        ComponentEventRequestParameters parameters = new ComponentEventRequestParameters("order/View", primaryPageName,
+                "gnip.gnop", "myevent", pageActivationContext, eventContext);
 
         expect(linkEncoder.createComponentEventLink(parameters, true)).andReturn(link);
 
         replay();
 
-        LinkSource source = new LinkSourceImpl(queue, collector, typeCoercer, null, linkEncoder,
-                null, null);
+        LinkSource source = new LinkSourceImpl(queue, collector, typeCoercer, null, linkEncoder, null, null,
+                emptyConfiguration);
 
-        assertSame(source.createComponentEventLink(primaryPage, "gnip.gnop", "myevent", true, 3, 5,
-                9), link);
+        assertSame(source.createComponentEventLink(primaryPage, "gnip.gnop", "myevent", true, 3, 5, 9), link);
 
         verify();
     }
 
-    private void testEventLinkCreation(String pageName, String nestedId, String eventType,
-            boolean forForm, Object... context)
+    private void testEventLinkCreation(String pageName, String nestedId, String eventType, boolean forForm,
+            Object... context)
     {
         Page primaryPage = mockPage();
         PageRenderQueue queue = mockPageRenderQueue();
@@ -241,8 +290,8 @@ public class LinkSourceImplTest extends 
 
         train_collectPageActivationContext(collector, pageName, "a", "b");
 
-        ComponentEventRequestParameters parameters = new ComponentEventRequestParameters(pageName,
-                pageName, nestedId, eventType, pageEventContext, eventContext);
+        ComponentEventRequestParameters parameters = new ComponentEventRequestParameters(pageName, pageName, nestedId,
+                eventType, pageEventContext, eventContext);
 
         expect(linkEncoder.createComponentEventLink(parameters, forForm)).andReturn(link);
 
@@ -250,13 +299,55 @@ public class LinkSourceImplTest extends 
 
         replay();
 
-        LinkSource source = new LinkSourceImpl(queue, collector, typeCoercer, null, linkEncoder,
-                null, null);
+        LinkSource source = new LinkSourceImpl(queue, collector, typeCoercer, null, linkEncoder, null, null,
+                emptyConfiguration);
 
         source.getLinkCreationHub().addListener(listener);
 
-        Link returnedLink = source.createComponentEventLink(primaryPage, nestedId, eventType,
-                forForm, context);
+        Link returnedLink = source.createComponentEventLink(primaryPage, nestedId, eventType, forForm, context);
+
+        // Make sure the same link is returned.
+
+        assertSame(returnedLink, link);
+
+        verify();
+    }
+
+    private void testEventLinkCreation2(String pageName, String nestedId, String eventType, boolean forForm,
+            Object... context)
+    {
+        Page primaryPage = mockPage();
+        PageRenderQueue queue = mockPageRenderQueue();
+        PageActivationContextCollector collector = mockPageActivationContextCollector();
+        LinkCreationListener2 listener = mockLinkCreationListener2();
+        ComponentEventLinkEncoder linkEncoder = mockComponentEventLinkEncoder();
+        Link link = mockLink();
+
+        ArrayEventContext eventContext = new ArrayEventContext(typeCoercer, context);
+
+        ArrayEventContext pageEventContext = new ArrayEventContext(typeCoercer, "a", "b");
+
+        train_getRenderingPage(queue, null);
+
+        train_getName(primaryPage, pageName);
+
+        train_collectPageActivationContext(collector, pageName, "a", "b");
+
+        ComponentEventRequestParameters parameters = new ComponentEventRequestParameters(pageName, pageName, nestedId,
+                eventType, pageEventContext, eventContext);
+
+        expect(linkEncoder.createComponentEventLink(parameters, forForm)).andReturn(link);
+
+        listener.createdComponentEventLink(link, parameters);
+
+        List<LinkCreationListener2> configuration = CollectionFactory.newList(listener);
+
+        replay();
+
+        LinkSource source = new LinkSourceImpl(queue, collector, typeCoercer, null, linkEncoder, null, null,
+                configuration);
+
+        Link returnedLink = source.createComponentEventLink(primaryPage, nestedId, eventType, forForm, context);
 
         // Make sure the same link is returned.
 
@@ -265,8 +356,8 @@ public class LinkSourceImplTest extends 
         verify();
     }
 
-    protected final void train_collectPageActivationContext(
-            PageActivationContextCollector collector, String pageName, Object... context)
+    protected final void train_collectPageActivationContext(PageActivationContextCollector collector, String pageName,
+            Object... context)
     {
         expect(collector.collectPageActivationContext(pageName)).andReturn(context);
     }