You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by iv...@apache.org on 2010/10/21 07:51:51 UTC

svn commit: r1025834 - in /wicket/trunk: wicket-util/src/main/java/org/apache/wicket/util/listener/ wicket-util/src/main/java/org/apache/wicket/util/watch/ wicket/src/main/java/org/apache/wicket/ wicket/src/main/java/org/apache/wicket/request/cycle/ wi...

Author: ivaynberg
Date: Thu Oct 21 05:51:50 2010
New Revision: 1025834

URL: http://svn.apache.org/viewvc?rev=1025834&view=rev
Log:
abstracted the idea of a listener collection
refactored listener lists to use the collection instead
removed detachlistener in favor of ondetach in request cycle listener
request cycle listener now gets the request cycle for context
Issue: WICKET-3125

Added:
    wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerCollection.java   (contents, props changed)
      - copied, changed from r1025821, wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerSet.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/AbstractRequestCycleListener.java   (with props)
    wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/IRequestCycleListener.java   (with props)
    wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycleListenerCollection.java   (with props)
Removed:
    wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/IListener.java
    wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerSet.java
Modified:
    wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ChangeListenerSet.java
    wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/IChangeListener.java
    wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/watch/ModificationWatcher.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/Application.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/Session.java
    wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java
    wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestCycleListenerTest.java

Modified: wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ChangeListenerSet.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ChangeListenerSet.java?rev=1025834&r1=1025833&r2=1025834&view=diff
==============================================================================
--- wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ChangeListenerSet.java (original)
+++ wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ChangeListenerSet.java Thu Oct 21 05:51:50 2010
@@ -24,14 +24,26 @@ package org.apache.wicket.util.listener;
  * 
  * @author Jonathan Locke
  */
-public final class ChangeListenerSet extends ListenerSet<IChangeListener>
+public final class ChangeListenerSet extends ListenerCollection<IChangeListener>
 {
 	/**
-	 * @see org.apache.wicket.util.listener.ListenerSet#notifyListener(org.apache.wicket.util.listener.IListener)
+	 * @see org.apache.wicket.util.listener.ListenerCollection#notifyListener(org.apache.wicket.util.listener.IListener)
 	 */
-	@Override
 	protected void notifyListener(IChangeListener listener)
 	{
-		listener.onChange();
+
+	}
+
+	public void notifyListeners()
+	{
+		notify(new INotifier<IChangeListener>()
+		{
+
+			public void notify(IChangeListener object)
+			{
+				object.onChange();
+			}
+
+		});
 	}
 }

Modified: wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/IChangeListener.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/IChangeListener.java?rev=1025834&r1=1025833&r2=1025834&view=diff
==============================================================================
--- wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/IChangeListener.java (original)
+++ wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/IChangeListener.java Thu Oct 21 05:51:50 2010
@@ -21,7 +21,7 @@ package org.apache.wicket.util.listener;
  * 
  * @author Jonathan Locke
  */
-public interface IChangeListener extends IListener
+public interface IChangeListener
 {
 	/**
 	 * Client method that is called to indicate that something changed.

Copied: wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerCollection.java (from r1025821, wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerSet.java)
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerCollection.java?p2=wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerCollection.java&p1=wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerSet.java&r1=1025821&r2=1025834&rev=1025834&view=diff
==============================================================================
--- wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerSet.java (original)
+++ wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerCollection.java Thu Oct 21 05:51:50 2010
@@ -16,50 +16,85 @@
  */
 package org.apache.wicket.util.listener;
 
-import java.util.HashSet;
-import java.util.Set;
+import java.io.Serializable;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * Holds a set of listeners implementing the IListener tag interface. Subclasses can implement
- * notification methods that cast the listener to the correct subinterface and invoke the
- * appropriate listener method.
- * 
- * Note that these classes are not meant to be serializable or for you to hold them in session (see
- * WICKET-2697)
+ * Represents a collection of listeners. Facilitates invocation of events on each listener.
+ * <p>
+ * NOTE: Ordering of listeners is not guaranteed and should not be relied upon
+ * </p>
  * 
+ * @autor ivaynberg (Igor Vaynberg)
  * @author Jonathan Locke
  * 
  * @param <T>
+ *            type of listeners
  */
-public abstract class ListenerSet<T extends IListener>
+public abstract class ListenerCollection<T> implements Serializable
 {
-	/** Set of change listeners */
-	private final Set<T> listeners = new HashSet<T>();
+	private static final Logger logger = LoggerFactory.getLogger(ListenerCollection.class);
+
+	/** list of listeners */
+	private final List<T> listeners = new CopyOnWriteArrayList<T>();
 
 	/**
 	 * Adds a listener to this set of listeners.
 	 * 
 	 * @param listener
 	 *            The listener to add
-	 * @return <tt>true</tt> if the set did not already contain the specified listener.
+	 * @return {@code true} if the listener was added
 	 */
 	public boolean add(final T listener)
 	{
-		return listeners.add(listener);
+		if (listener == null && !isAllowingNulls())
+		{
+			return false;
+		}
+		if (!isAllowingDuplicates() && listeners.contains(listener))
+		{
+			return false;
+		}
+		listeners.add(listener);
+		return true;
 	}
 
 	/**
-	 * Notifies each listener in this set by calling notifyListener.
+	 * Notifies each listener in this
+	 * 
+	 * @param notifier
+	 *            notifier used to notify each listener
 	 */
-	public void notifyListeners()
+	protected void notify(INotifier<T> notifier)
 	{
-		// Create a stable copy for iterating over
-		final Set<T> copy = new HashSet<T>(listeners);
+		for (T listener : listeners)
+		{
+			notifier.notify(listener);
+		}
+	}
 
-		// Notify all listeners that the file changed
-		for (T listener : copy)
+	/**
+	 * Notifies each listener in this set ignoring exceptions. Exceptions will be logged.
+	 * 
+	 * @param notifier
+	 *            notifier used to notify each listener
+	 */
+	protected void notifyIgnoringExceptions(INotifier<T> notifier)
+	{
+		for (T listener : listeners)
 		{
-			notifyListener(listener);
+			try
+			{
+				notifier.notify(listener);
+			}
+			catch (Exception e)
+			{
+				logger.error("Error invoking listener: " + listener, e);
+			}
 		}
 	}
 
@@ -75,10 +110,35 @@ public abstract class ListenerSet<T exte
 	}
 
 	/**
-	 * Notifies a listener.
+	 * Whether or not added listeners should be checked for duplicates.
 	 * 
-	 * @param listener
-	 *            The listener to notify
+	 * @return {@code true} to ignore duplicates
 	 */
-	protected abstract void notifyListener(T listener);
+	protected boolean isAllowingDuplicates()
+	{
+		return true;
+	}
+
+	/**
+	 * Whether or not to allow {@code null}s in listener collection.
+	 * 
+	 * @return {@code} true to allow nulls to be added to the collection
+	 */
+	protected boolean isAllowingNulls()
+	{
+		return false;
+	}
+
+	/**
+	 * Used to notify a listener. Usually this method simply forwards the {@link #notify(Object)} to
+	 * the proper method on the listener.
+	 * 
+	 * @author ivaynberg (Igor Vaynberg)
+	 * @param <T>
+	 */
+	protected static interface INotifier<T>
+	{
+		void notify(T listener);
+	}
+
 }

Propchange: wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerCollection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/listener/ListenerCollection.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/watch/ModificationWatcher.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/watch/ModificationWatcher.java?rev=1025834&r1=1025833&r2=1025834&view=diff
==============================================================================
--- wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/watch/ModificationWatcher.java (original)
+++ wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/watch/ModificationWatcher.java Thu Oct 21 05:51:50 2010
@@ -118,7 +118,7 @@ public class ModificationWatcher impleme
 		else
 		{
 			// Add listener to existing entry
-			return entry.listeners.add(listener);
+			return !entry.listeners.add(listener);
 		}
 	}
 

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/Application.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/Application.java?rev=1025834&r1=1025833&r2=1025834&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/Application.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/Application.java Thu Oct 21 05:51:50 2010
@@ -65,9 +65,11 @@ import org.apache.wicket.request.IReques
 import org.apache.wicket.request.Request;
 import org.apache.wicket.request.Response;
 import org.apache.wicket.request.component.IRequestablePage;
+import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
 import org.apache.wicket.request.cycle.RequestCycle;
-import org.apache.wicket.request.cycle.RequestCycle.IRequestCycleListener;
 import org.apache.wicket.request.cycle.RequestCycleContext;
+import org.apache.wicket.request.cycle.RequestCycleListenerCollection;
 import org.apache.wicket.request.mapper.CompoundRequestMapper;
 import org.apache.wicket.request.mapper.ICompoundRequestMapper;
 import org.apache.wicket.request.mapper.IMapperContext;
@@ -170,7 +172,7 @@ public abstract class Application implem
 	/** */
 	private List<IComponentOnAfterRenderListener> componentOnAfterRenderListeners;
 
-	private final List<IRequestCycleListener> requestCycleListeners = new ArrayList<IRequestCycleListener>();
+	private final RequestCycleListenerCollection requestCycleListeners = new RequestCycleListenerCollection();
 
 	/** root mapper */
 	private IRequestMapper rootRequestMapper;
@@ -1165,9 +1167,9 @@ public abstract class Application implem
 	/**
 	 * @return the unmodifiable request list of {@link IRequestCycleListener}s in this application
 	 */
-	public List<IRequestCycleListener> getRequestCycleListeners()
+	public RequestCycleListenerCollection getRequestCycleListeners()
 	{
-		return Collections.unmodifiableList(requestCycleListeners);
+		return requestCycleListeners;
 	}
 
 
@@ -1493,7 +1495,8 @@ public abstract class Application implem
 			getRootRequestMapper(), newExceptionMapper());
 
 		RequestCycle requestCycle = getRequestCycleProvider().get(context);
-		requestCycle.register(new RequestCycle.DetachCallback()
+		requestCycle.getListeners().add(requestCycleListeners);
+		requestCycle.getListeners().add(new AbstractRequestCycleListener()
 		{
 			public void onDetach(RequestCycle requestCycle)
 			{

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/Session.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/Session.java?rev=1025834&r1=1025833&r2=1025834&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/Session.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/Session.java Thu Oct 21 05:51:50 2010
@@ -33,6 +33,7 @@ import org.apache.wicket.feedback.Feedba
 import org.apache.wicket.page.IPageManager;
 import org.apache.wicket.request.ClientInfo;
 import org.apache.wicket.request.Request;
+import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.session.ISessionStore;
 import org.apache.wicket.util.lang.Objects;
@@ -440,9 +441,10 @@ public abstract class Session implements
 	{
 		if (sessionInvalidated == false)
 		{
-			RequestCycle.get().register(new RequestCycle.DetachCallback()
+			RequestCycle.get().getListeners().add(new AbstractRequestCycleListener()
 			{
-				public void onDetach(final RequestCycle requestCycle)
+				@Override
+				public void onDetach(final RequestCycle cycle)
 				{
 					destroy();
 				}

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/AbstractRequestCycleListener.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/AbstractRequestCycleListener.java?rev=1025834&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/AbstractRequestCycleListener.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/AbstractRequestCycleListener.java Thu Oct 21 05:51:50 2010
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.request.cycle;
+
+public abstract class AbstractRequestCycleListener implements IRequestCycleListener
+{
+	public void onBeginRequest(final RequestCycle cycle)
+	{
+	}
+
+	public void onDetach(final RequestCycle cycle)
+	{
+	}
+
+	public void onEndRequest(final RequestCycle cycle)
+	{
+	}
+
+	public void onException(final RequestCycle cycle, Exception ex)
+	{
+	}
+}

Propchange: wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/AbstractRequestCycleListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/IRequestCycleListener.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/IRequestCycleListener.java?rev=1025834&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/IRequestCycleListener.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/IRequestCycleListener.java Thu Oct 21 05:51:50 2010
@@ -0,0 +1,55 @@
+package org.apache.wicket.request.cycle;
+
+import org.apache.wicket.Application;
+
+/**
+ * A callback interface for various methods in the request cycle. If you are creating a framework
+ * that needs to do something in this methods, rather than extending RequestCycle or one of its
+ * subclasses, you should implement this callback and allow users to add your listener to their
+ * custom request cycle.
+ * 
+ * These listeners can be added directly to the request cycle when it is created or to the
+ * {@link Application}
+ * 
+ * @author Jeremy Thomerson
+ * @see Application#addRequestCycleListener(IRequestCycleListener)
+ * @see RequestCycle#register(IRequestCycleListener)
+ */
+public interface IRequestCycleListener
+{
+	/**
+	 * Called when the request cycle object is beginning its response
+	 * 
+	 * @param cycle
+	 */
+	void onBeginRequest(RequestCycle cycle);
+
+	/**
+	 * Called when the request cycle object has finished its response
+	 * 
+	 * @param cycle
+	 */
+	void onEndRequest(RequestCycle cycle);
+
+	/**
+	 * Called after the request cycle has been detached
+	 * 
+	 * @param cycle
+	 */
+	void onDetach(RequestCycle cycle);
+
+	/**
+	 * Called when there is an exception in the request cycle that would normally be handled by
+	 * {@link RequestCycle#handleException(Exception)}
+	 * 
+	 * Note that in the event of an exception, {@link #onEndRequest()} will still be called after
+	 * these listeners have {@link #onException(Exception)} called
+	 * 
+	 * @param cycle
+	 * 
+	 * @param ex
+	 *            the exception that was passed in to
+	 *            {@link RequestCycle#handleException(Exception)}
+	 */
+	void onException(RequestCycle cycle, Exception ex);
+}
\ No newline at end of file

Propchange: wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/IRequestCycleListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java?rev=1025834&r1=1025833&r2=1025834&view=diff
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java (original)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java Thu Oct 21 05:51:50 2010
@@ -16,10 +16,6 @@
  */
 package org.apache.wicket.request.cycle;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.wicket.Application;
 import org.apache.wicket.MetaDataEntry;
 import org.apache.wicket.MetaDataKey;
 import org.apache.wicket.Page;
@@ -71,61 +67,6 @@ public class RequestCycle extends Reques
 
 	private boolean cleanupFeedbackMessagesOnDetach = true;
 
-	/**
-	 * Custom callback invoked on request cycle detach. Detach callbacks are invoked after all
-	 * {@link IRequestHandler}s are detached.
-	 * 
-	 * @author Matej Knopp
-	 */
-	public interface DetachCallback
-	{
-		/**
-		 * Invoked on request cycle detach.
-		 * 
-		 * @param requestCycle
-		 */
-		public void onDetach(RequestCycle requestCycle);
-	}
-
-	/**
-	 * A callback interface for various methods in the request cycle. If you are creating a
-	 * framework that needs to do something in this methods, rather than extending RequestCycle or
-	 * one of its subclasses, you should implement this callback and allow users to add your
-	 * listener to their custom request cycle.
-	 * 
-	 * These listeners can be added directly to the request cycle when it is created or to the
-	 * {@link Application}
-	 * 
-	 * @author Jeremy Thomerson
-	 * @see Application#addRequestCycleListener(IRequestCycleListener)
-	 * @see RequestCycle#register(IRequestCycleListener)
-	 */
-	public interface IRequestCycleListener
-	{
-		/**
-		 * Called when the request cycle object is beginning its response
-		 */
-		void onBeginRequest();
-
-		/**
-		 * Called when the request cycle object has finished its response
-		 */
-		void onEndRequest();
-
-		/**
-		 * Called when there is an exception in the request cycle that would normally be handled by
-		 * {@link RequestCycle#handleException(Exception)}
-		 * 
-		 * Note that in the event of an exception, {@link #onEndRequest()} will still be called
-		 * after these listeners have {@link #onException(Exception)} called
-		 * 
-		 * @param ex
-		 *            the exception that was passed in to
-		 *            {@link RequestCycle#handleException(Exception)}
-		 */
-		void onException(Exception ex);
-	}
-
 	private interface IExecutor<T>
 	{
 
@@ -160,9 +101,7 @@ public class RequestCycle extends Reques
 
 	private final IExceptionMapper exceptionMapper;
 
-	private final List<DetachCallback> detachCallbacks = new ArrayList<DetachCallback>();
-
-	private final List<IRequestCycleListener> requestCycleListeners = new ArrayList<IRequestCycleListener>();
+	private final RequestCycleListenerCollection listeners = new RequestCycleListenerCollection();
 
 	private UrlRenderer urlRenderer;
 
@@ -304,13 +243,7 @@ public class RequestCycle extends Reques
 		boolean result;
 		try
 		{
-			callRequestCycleListeners(new IExecutor<IRequestCycleListener>()
-			{
-				public void execute(IRequestCycleListener rcl)
-				{
-					rcl.onBeginRequest();
-				}
-			}, "onBeginRequest");
+			listeners.onBeginRequest(this);
 			onBeginRequest();
 			result = processRequest();
 		}
@@ -321,26 +254,6 @@ public class RequestCycle extends Reques
 		return result;
 	}
 
-	private void callRequestCycleListeners(IExecutor<IRequestCycleListener> executor, String method)
-	{
-		List<IRequestCycleListener> app = Application.get().getRequestCycleListeners();
-		int size = requestCycleListeners.size() + app.size();
-		List<IRequestCycleListener> listeners = new ArrayList<IRequestCycleListener>(size);
-		listeners.addAll(app);
-		listeners.addAll(requestCycleListeners);
-		for (IRequestCycleListener rcl : listeners)
-		{
-			try
-			{
-				executor.execute(rcl);
-			}
-			catch (Exception ex)
-			{
-				log.error("Error executing " + method + " on IRequestCycleListener", ex);
-			}
-		}
-	}
-
 	/**
 	 * 
 	 * @param handler
@@ -377,13 +290,7 @@ public class RequestCycle extends Reques
 	 */
 	protected IRequestHandler handleException(final Exception e)
 	{
-		callRequestCycleListeners(new IExecutor<RequestCycle.IRequestCycleListener>()
-		{
-			public void execute(IRequestCycleListener object)
-			{
-				object.onException(e);
-			}
-		}, "onException");
+		listeners.onException(this, e);
 		return exceptionMapper.map(e);
 	}
 
@@ -578,14 +485,8 @@ public class RequestCycle extends Reques
 
 		try
 		{
-			callRequestCycleListeners(new IExecutor<IRequestCycleListener>()
-			{
-				public void execute(IRequestCycleListener rcl)
-				{
-					rcl.onEndRequest();
-				}
-			}, "onEndRequest");
 			onEndRequest();
+			listeners.onEndRequest(this);
 		}
 		catch (RuntimeException e)
 		{
@@ -598,43 +499,12 @@ public class RequestCycle extends Reques
 		}
 		finally
 		{
-			for (DetachCallback c : detachCallbacks)
-			{
-				try
-				{
-					c.onDetach(this);
-				}
-				catch (Exception e)
-				{
-					log.error("Error detaching DetachCallback", e);
-				}
-			}
+			listeners.onDetach(this);
 			set(null);
 		}
 	}
 
 	/**
-	 * Registers a callback to be invoked on {@link RequestCycle} detach. The callback will be
-	 * invoked after all {@link IRequestHandler}s are detached.
-	 * 
-	 * @param detachCallback
-	 */
-	public void register(DetachCallback detachCallback)
-	{
-		detachCallbacks.add(detachCallback);
-	}
-
-	/**
-	 * Registers a listener to extend functionality in the {@link RequestCycle}.
-	 * 
-	 * @param listener
-	 */
-	public void register(IRequestCycleListener listener)
-	{
-		requestCycleListeners.add(listener);
-	}
-
-	/**
 	 * Convenience method for setting next page to be rendered.
 	 * 
 	 * @param page
@@ -721,5 +591,9 @@ public class RequestCycle extends Reques
 	{
 	}
 
+	public RequestCycleListenerCollection getListeners()
+	{
+		return listeners;
+	}
 
 }

Added: wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycleListenerCollection.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycleListenerCollection.java?rev=1025834&view=auto
==============================================================================
--- wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycleListenerCollection.java (added)
+++ wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycleListenerCollection.java Thu Oct 21 05:51:50 2010
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.request.cycle;
+
+import org.apache.wicket.util.listener.ListenerCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RequestCycleListenerCollection extends ListenerCollection<IRequestCycleListener>
+	implements
+		IRequestCycleListener
+{
+	private static final Logger logger = LoggerFactory.getLogger(RequestCycleListenerCollection.class);
+
+	public void onBeginRequest(final RequestCycle cycle)
+	{
+		notify(new INotifier<IRequestCycleListener>()
+		{
+			public void notify(IRequestCycleListener listener)
+			{
+				listener.onBeginRequest(cycle);
+			}
+		});
+	}
+
+	public void onEndRequest(final RequestCycle cycle)
+	{
+		notify(new INotifier<IRequestCycleListener>()
+		{
+			public void notify(IRequestCycleListener listener)
+			{
+				listener.onEndRequest(cycle);
+			}
+		});
+	}
+
+	public void onException(final RequestCycle cycle, final Exception ex)
+	{
+		notify(new INotifier<IRequestCycleListener>()
+		{
+			public void notify(IRequestCycleListener listener)
+			{
+				listener.onException(cycle, ex);
+			}
+		});
+
+	}
+
+	public void onDetach(final RequestCycle cycle)
+	{
+		notify(new INotifier<IRequestCycleListener>()
+		{
+			public void notify(IRequestCycleListener listener)
+			{
+				try
+				{
+					listener.onDetach(cycle);
+				}
+				catch (Exception e)
+				{
+					logger.error("Error detaching request cycle listener: " + listener, e);
+				}
+			}
+		});
+	}
+
+}

Propchange: wicket/trunk/wicket/src/main/java/org/apache/wicket/request/cycle/RequestCycleListenerCollection.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestCycleListenerTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestCycleListenerTest.java?rev=1025834&r1=1025833&r2=1025834&view=diff
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestCycleListenerTest.java (original)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestCycleListenerTest.java Thu Oct 21 05:51:50 2010
@@ -26,7 +26,6 @@ import org.apache.wicket.request.IReques
 import org.apache.wicket.request.Request;
 import org.apache.wicket.request.Response;
 import org.apache.wicket.request.Url;
-import org.apache.wicket.request.cycle.RequestCycle.IRequestCycleListener;
 import org.apache.wicket.resource.DummyApplication;
 
 /**
@@ -34,20 +33,21 @@ import org.apache.wicket.resource.DummyA
  */
 public class RequestCycleListenerTest extends BaseRequestHandlerStackTest
 {
-	private int begins, ends, exceptions, exceptionsMapped, responses, detaches = 0;
 
-	private RequestCycle cycle;
+
+	private int begins, ends, exceptions, exceptionsMapped, responses, detaches,
+		detachesnotified = 0;
+
 	private IRequestHandler handler;
 
 	@Override
 	protected void setUp() throws Exception
 	{
 		super.setUp();
-		setupNewRequestCycle(false);
 		ThreadContext.setApplication(new DummyApplication());
 	}
 
-	private void setupNewRequestCycle(final boolean throwExceptionInRespond)
+	private RequestCycle newRequestCycle(final boolean throwExceptionInRespond)
 	{
 		final Response originalResponse = newResponse();
 		Request request = new MockWebRequest(Url.parse("http://wicket.apache.org"));
@@ -94,7 +94,15 @@ public class RequestCycleListenerTest ex
 		};
 		RequestCycleContext context = new RequestCycleContext(request, originalResponse,
 			requestMapper, exceptionMapper);
-		cycle = new RequestCycle(context);
+
+		RequestCycle cycle = new RequestCycle(context);
+
+		if (Application.exists())
+		{
+			cycle.getListeners().add(Application.get().getRequestCycleListeners());
+		}
+
+		return cycle;
 	}
 
 	/**
@@ -102,19 +110,22 @@ public class RequestCycleListenerTest ex
 	 */
 	public void testBasicOperations() throws Exception
 	{
-		Application.get().addRequestCycleListener(newIncrementingListener());
-		assertValues(0, 0, 0, 0, 0, 0);
+		Application.get().addRequestCycleListener(new IncrementingListener());
+
+		RequestCycle cycle = newRequestCycle(false);
+
+		assertValues(0, 0, 0, 0, 0, 0, 0);
 		/*
 		 * begins, ends, responses and detaches should increment by one because we have one listener
 		 * 
 		 * exceptions should not increment because none are thrown
 		 */
 		cycle.processRequestAndDetach();
-		assertValues(1, 1, 0, 0, 1, 1);
+		assertValues(1, 1, 0, 0, 1, 1, 1);
 
 		// TEST WITH TWO LISTENERS
-		setupNewRequestCycle(false);
-		cycle.register(newIncrementingListener());
+		cycle = newRequestCycle(false);
+		cycle.getListeners().add(new IncrementingListener());
 		/*
 		 * we now have two listeners (app and cycle)
 		 * 
@@ -125,11 +136,11 @@ public class RequestCycleListenerTest ex
 		 * responses and detaches should increment by one
 		 */
 		cycle.processRequestAndDetach();
-		assertValues(3, 3, 0, 0, 2, 2);
+		assertValues(3, 3, 0, 0, 2, 2, 3);
 
 		// TEST WITH TWO LISTENERS AND AN EXCEPTION DURING RESPONSE
-		setupNewRequestCycle(true);
-		cycle.register(newIncrementingListener());
+		cycle = newRequestCycle(true);
+		cycle.getListeners().add(new IncrementingListener());
 		/*
 		 * begins and ends should increment by two (once for each listener)
 		 * 
@@ -142,11 +153,35 @@ public class RequestCycleListenerTest ex
 		 * detaches should increment by one
 		 */
 		cycle.processRequestAndDetach();
-		assertValues(5, 5, 2, 1, 2, 3);
+		assertValues(5, 5, 2, 1, 2, 3, 5);
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testExceptionHandingInOnDetach() throws Exception
+	{
+		// this test is a little flaky because it depends on the ordering of listeners which is not
+		// guaranteed
+		RequestCycle cycle = newRequestCycle(false);
+		cycle.getListeners().add(new IncrementingListener());
+		cycle.getListeners().add(new IncrementingListener()
+		{
+			@Override
+			public void onDetach(final RequestCycle cycle)
+			{
+				super.onDetach(cycle);
+				throw new RuntimeException();
+			}
+		});
+		cycle.getListeners().add(new IncrementingListener());
+		cycle.processRequestAndDetach();
+		assertValues(3, 3, 0, 0, 1, 1, 3);
 	}
 
+
 	private void assertValues(int begins, int ends, int exceptions, int exceptionsMapped,
-		int responses, int detaches)
+		int responses, int detaches, int detachesnotified)
 	{
 		assertEquals(begins, this.begins);
 		assertEquals(ends, this.ends);
@@ -154,26 +189,31 @@ public class RequestCycleListenerTest ex
 		assertEquals(exceptionsMapped, this.exceptionsMapped);
 		assertEquals(responses, this.responses);
 		assertEquals(detaches, this.detaches);
+		assertEquals(detachesnotified, this.detachesnotified);
 	}
 
-	private IRequestCycleListener newIncrementingListener()
+	private class IncrementingListener implements IRequestCycleListener
 	{
-		return new IRequestCycleListener()
+		public void onException(final RequestCycle cycle, Exception ex)
 		{
-			public void onException(Exception ex)
-			{
-				exceptions++;
-			}
+			exceptions++;
+		}
 
-			public void onEndRequest()
-			{
-				ends++;
-			}
+		public void onEndRequest(final RequestCycle cycle)
+		{
+			ends++;
+		}
 
-			public void onBeginRequest()
-			{
-				begins++;
-			}
-		};
+		public void onBeginRequest(final RequestCycle cycle)
+		{
+			begins++;
+		}
+
+		public void onDetach(final RequestCycle cycle)
+		{
+			detachesnotified++;
+		}
 	}
+
+
 }