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

svn commit: r1025813 - in /wicket/trunk/wicket/src: main/java/org/apache/wicket/ main/java/org/apache/wicket/request/cycle/ test/java/org/apache/wicket/request/cycle/

Author: jrthomerson
Date: Thu Oct 21 02:37:11 2010
New Revision: 1025813

URL: http://svn.apache.org/viewvc?rev=1025813&view=rev
Log:
fixes WICKET-3125

Added:
    wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/BaseRequestHandlerStackTest.java
    wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestCycleListenerTest.java
Modified:
    wicket/trunk/wicket/src/main/java/org/apache/wicket/Application.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/RequestHandlerStackTest.java

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=1025813&r1=1025812&r2=1025813&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 02:37:11 2010
@@ -66,6 +66,7 @@ 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.RequestCycle;
+import org.apache.wicket.request.cycle.RequestCycle.IRequestCycleListener;
 import org.apache.wicket.request.cycle.RequestCycleContext;
 import org.apache.wicket.request.mapper.CompoundRequestMapper;
 import org.apache.wicket.request.mapper.ICompoundRequestMapper;
@@ -169,6 +170,8 @@ public abstract class Application implem
 	/** */
 	private List<IComponentOnAfterRenderListener> componentOnAfterRenderListeners;
 
+	private final List<IRequestCycleListener> requestCycleListeners = new ArrayList<IRequestCycleListener>();
+
 	/** root mapper */
 	private IRequestMapper rootRequestMapper;
 
@@ -1150,6 +1153,25 @@ public abstract class Application implem
 	}
 
 	/**
+	 * Registers a listener to extend functionality in the {@link RequestCycle}.
+	 * 
+	 * @param listener
+	 */
+	public void addRequestCycleListener(IRequestCycleListener listener)
+	{
+		requestCycleListeners.add(listener);
+	}
+
+	/**
+	 * @return the unmodifiable request list of {@link IRequestCycleListener}s in this application
+	 */
+	public List<IRequestCycleListener> getRequestCycleListeners()
+	{
+		return Collections.unmodifiableList(requestCycleListeners);
+	}
+
+
+	/**
 	 * Removes an {@link IComponentOnAfterRenderListener}.
 	 * 
 	 * @param listener

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=1025813&r1=1025812&r2=1025813&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 02:37:11 2010
@@ -19,6 +19,7 @@ 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;
@@ -87,6 +88,51 @@ public class RequestCycle extends Reques
 	}
 
 	/**
+	 * 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>
+	{
+
+		void execute(T object);
+	}
+
+	/**
 	 * Returns request cycle associated with current thread.
 	 * 
 	 * @return request cycle instance or <code>null</code> if no request cycle is associated with
@@ -116,6 +162,8 @@ public class RequestCycle extends Reques
 
 	private final List<DetachCallback> detachCallbacks = new ArrayList<DetachCallback>();
 
+	private final List<IRequestCycleListener> requestCycleListeners = new ArrayList<IRequestCycleListener>();
+
 	private UrlRenderer urlRenderer;
 
 	/** MetaDataEntry array. */
@@ -256,6 +304,13 @@ public class RequestCycle extends Reques
 		boolean result;
 		try
 		{
+			callRequestCycleListeners(new IExecutor<IRequestCycleListener>()
+			{
+				public void execute(IRequestCycleListener rcl)
+				{
+					rcl.onBeginRequest();
+				}
+			}, "onBeginRequest");
 			onBeginRequest();
 			result = processRequest();
 		}
@@ -266,6 +321,26 @@ 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
@@ -302,6 +377,13 @@ 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");
 		return exceptionMapper.map(e);
 	}
 
@@ -496,6 +578,13 @@ public class RequestCycle extends Reques
 
 		try
 		{
+			callRequestCycleListeners(new IExecutor<IRequestCycleListener>()
+			{
+				public void execute(IRequestCycleListener rcl)
+				{
+					rcl.onEndRequest();
+				}
+			}, "onEndRequest");
 			onEndRequest();
 		}
 		catch (RuntimeException e)
@@ -536,6 +625,16 @@ public class RequestCycle extends Reques
 	}
 
 	/**
+	 * 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
@@ -574,11 +673,22 @@ public class RequestCycle extends Reques
 			RenderPageRequestHandler.RedirectPolicy.AUTO_REDIRECT));
 	}
 
+	/**
+	 * Gets whether or not feedback messages are to be cleaned up on detach.
+	 * 
+	 * @return true if they are
+	 */
 	public boolean isCleanupFeedbackMessagesOnDetach()
 	{
 		return cleanupFeedbackMessagesOnDetach;
 	}
 
+	/**
+	 * Sets whether or not feedback messages should be cleaned up on detach.
+	 * 
+	 * @param cleanupFeedbackMessagesOnDetach
+	 *            true if you want them to be cleaned up
+	 */
 	public void setCleanupFeedbackMessagesOnDetach(boolean cleanupFeedbackMessagesOnDetach)
 	{
 		this.cleanupFeedbackMessagesOnDetach = cleanupFeedbackMessagesOnDetach;

Added: wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/BaseRequestHandlerStackTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/BaseRequestHandlerStackTest.java?rev=1025813&view=auto
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/BaseRequestHandlerStackTest.java (added)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/BaseRequestHandlerStackTest.java Thu Oct 21 02:37:11 2010
@@ -0,0 +1,64 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import org.apache.wicket.request.RequestHandlerStack;
+import org.apache.wicket.request.Response;
+
+/**
+ * @author Jeremy Thomerson
+ */
+public class BaseRequestHandlerStackTest extends TestCase
+{
+
+	protected Response newResponse()
+	{
+		return new Response()
+		{
+			@Override
+			public void write(byte[] array)
+			{
+			}
+
+			@Override
+			public void write(CharSequence sequence)
+			{
+			}
+
+			@Override
+			public String encodeURL(CharSequence url)
+			{
+				return null;
+			}
+		};
+	}
+
+	protected RequestHandlerStack newStack(Response response)
+	{
+		return new RequestHandlerStack(response)
+		{
+			@Override
+			protected RequestCycle getRequestCycle()
+			{
+				return null;
+			}
+		};
+	}
+
+}

Added: 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=1025813&view=auto
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestCycleListenerTest.java (added)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestCycleListenerTest.java Thu Oct 21 02:37:11 2010
@@ -0,0 +1,179 @@
+/*
+ * 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.Application;
+import org.apache.wicket.ThreadContext;
+import org.apache.wicket.mock.MockWebRequest;
+import org.apache.wicket.request.IExceptionMapper;
+import org.apache.wicket.request.IRequestCycle;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.IRequestMapper;
+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;
+
+/**
+ * @author Jeremy Thomerson
+ */
+public class RequestCycleListenerTest extends BaseRequestHandlerStackTest
+{
+	private int begins, ends, exceptions, exceptionsMapped, responses, detaches = 0;
+
+	private RequestCycle cycle;
+	private IRequestHandler handler;
+
+	@Override
+	protected void setUp() throws Exception
+	{
+		super.setUp();
+		setupNewRequestCycle(false);
+		ThreadContext.setApplication(new DummyApplication());
+	}
+
+	private void setupNewRequestCycle(final boolean throwExceptionInRespond)
+	{
+		final Response originalResponse = newResponse();
+		Request request = new MockWebRequest(Url.parse("http://wicket.apache.org"));
+		handler = new IRequestHandler()
+		{
+			public void respond(IRequestCycle requestCycle)
+			{
+				if (throwExceptionInRespond)
+				{
+					throw new RuntimeException("testing purposes only");
+				}
+				responses++;
+			}
+
+			public void detach(IRequestCycle requestCycle)
+			{
+				detaches++;
+			}
+		};
+		IRequestMapper requestMapper = new IRequestMapper()
+		{
+			public IRequestHandler mapRequest(Request request)
+			{
+				return handler;
+			}
+
+			public Url mapHandler(IRequestHandler requestHandler)
+			{
+				throw new UnsupportedOperationException();
+			}
+
+			public int getCompatibilityScore(Request request)
+			{
+				throw new UnsupportedOperationException();
+			}
+		};
+		IExceptionMapper exceptionMapper = new IExceptionMapper()
+		{
+			public IRequestHandler map(Exception e)
+			{
+				exceptionsMapped++;
+				return null;
+			}
+		};
+		RequestCycleContext context = new RequestCycleContext(request, originalResponse,
+			requestMapper, exceptionMapper);
+		cycle = new RequestCycle(context);
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public void testBasicOperations() throws Exception
+	{
+		Application.get().addRequestCycleListener(newIncrementingListener());
+		assertValues(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);
+
+		// TEST WITH TWO LISTENERS
+		setupNewRequestCycle(false);
+		cycle.register(newIncrementingListener());
+		/*
+		 * we now have two listeners (app and cycle)
+		 * 
+		 * begins and ends should increment by two (once for each listener)
+		 * 
+		 * exceptions should not increment because none are thrown
+		 * 
+		 * responses and detaches should increment by one
+		 */
+		cycle.processRequestAndDetach();
+		assertValues(3, 3, 0, 0, 2, 2);
+
+		// TEST WITH TWO LISTENERS AND AN EXCEPTION DURING RESPONSE
+		setupNewRequestCycle(true);
+		cycle.register(newIncrementingListener());
+		/*
+		 * begins and ends should increment by two (once for each listener)
+		 * 
+		 * exceptions should increment by two (once for each listener)
+		 * 
+		 * exceptionsMapped should increment by one
+		 * 
+		 * responses should not increment because of the error
+		 * 
+		 * detaches should increment by one
+		 */
+		cycle.processRequestAndDetach();
+		assertValues(5, 5, 2, 1, 2, 3);
+	}
+
+	private void assertValues(int begins, int ends, int exceptions, int exceptionsMapped,
+		int responses, int detaches)
+	{
+		assertEquals(begins, this.begins);
+		assertEquals(ends, this.ends);
+		assertEquals(exceptions, this.exceptions);
+		assertEquals(exceptionsMapped, this.exceptionsMapped);
+		assertEquals(responses, this.responses);
+		assertEquals(detaches, this.detaches);
+	}
+
+	private IRequestCycleListener newIncrementingListener()
+	{
+		return new IRequestCycleListener()
+		{
+			public void onException(Exception ex)
+			{
+				exceptions++;
+			}
+
+			public void onEndRequest()
+			{
+				ends++;
+			}
+
+			public void onBeginRequest()
+			{
+				begins++;
+			}
+		};
+	}
+}

Modified: wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestHandlerStackTest.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestHandlerStackTest.java?rev=1025813&r1=1025812&r2=1025813&view=diff
==============================================================================
--- wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestHandlerStackTest.java (original)
+++ wicket/trunk/wicket/src/test/java/org/apache/wicket/request/cycle/RequestHandlerStackTest.java Thu Oct 21 02:37:11 2010
@@ -16,8 +16,6 @@
  */
 package org.apache.wicket.request.cycle;
 
-import junit.framework.TestCase;
-
 import org.apache.wicket.request.IRequestCycle;
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.RequestHandlerStack;
@@ -27,7 +25,7 @@ import org.apache.wicket.request.Respons
  * 
  * @author Matej Knopp
  */
-public class RequestHandlerStackTest extends TestCase
+public class RequestHandlerStackTest extends BaseRequestHandlerStackTest
 {
 
 	/**
@@ -37,40 +35,6 @@ public class RequestHandlerStackTest ext
 	{
 	}
 
-	private Response newResponse()
-	{
-		return new Response()
-		{
-			@Override
-			public void write(byte[] array)
-			{
-			}
-
-			@Override
-			public void write(CharSequence sequence)
-			{
-			}
-
-			@Override
-			public String encodeURL(CharSequence url)
-			{
-				return null;
-			}
-		};
-	}
-
-	private RequestHandlerStack newStack(Response response)
-	{
-		return new RequestHandlerStack(response)
-		{
-			@Override
-			protected RequestCycle getRequestCycle()
-			{
-				return null;
-			}
-		};
-	}
-
 	private boolean testFlag1;
 	private boolean testFlag2;
 	private boolean testFlag3;
@@ -268,7 +232,6 @@ public class RequestHandlerStackTest ext
 		assertTrue(detachedFlag4);
 	}
 
-
 	/**
 	 * 
 	 */
@@ -301,10 +264,10 @@ public class RequestHandlerStackTest ext
 				testFlag3 = false;
 				stack.scheduleRequestHandlerAfterCurrent(handler4);
 
-				// make sure that handler4's respond method is fired after this one ends
+				// make sure that handler4's respond method is fired after this
+				// one ends
 				testFlag4 = false;
 
-
 				// code must be be reached
 				testFlag3 = true;
 			}