FELIX-4548 : Implement the missing errors registration. Apply modified patch from Thomas Baier. Traversing the exception hierarchy by using the exception object (instread of the class name)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1672326 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
index c675987..d3ea3b1 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
@@ -166,8 +166,8 @@
code != SC_PARTIAL_CONTENT &&
code >= SC_OK)
{
- final String exceptionType = (String)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
- final ServletHandler errorHandler = handlerRegistry.getErrorsHandler(request.getRequestURI(), this.serviceId, code, exceptionType);
+ final Throwable exception = (Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+ final ServletHandler errorHandler = handlerRegistry.getErrorsHandler(request.getRequestURI(), this.serviceId, code, exception);
if ( errorHandler != null )
{
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
index b319c52..a41ce46 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
@@ -94,15 +94,51 @@
this.exceptionsMap.clear();
}
+ /**
+ * Get the servlet handling the error
+ * @param exception Optional exception
+ * @param errorCode Error code
+ * @return The servlet handling the error or {@code null}
+ */
+ public ServletHandler get(final Throwable exception, final int errorCode)
+ {
+ ServletHandler errorHandler = this.get(exception);
+ if (errorHandler != null)
+ {
+ return errorHandler;
+ }
- public ServletHandler get(int errorCode)
+ return get(errorCode);
+ }
+
+ private ServletHandler get(final int errorCode)
{
return this.errorCodesMap.get(errorCode);
}
- public ServletHandler get(String exception)
+ private ServletHandler get(final Throwable exception)
{
- return this.exceptionsMap.get(exception);
+ if (exception == null)
+ {
+ return null;
+ }
+
+ ServletHandler servletHandler = null;
+ Class<?> throwableClass = exception.getClass();
+ while ( servletHandler == null && throwableClass != null )
+ {
+ servletHandler = this.exceptionsMap.get(throwableClass.getName());
+ if ( servletHandler == null )
+ {
+ throwableClass = throwableClass.getSuperclass();
+ if ( !Throwable.class.isAssignableFrom(throwableClass) )
+ {
+ throwableClass = null;
+ }
+ }
+
+ }
+ return servletHandler;
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
index 02f68ea..8fa9788 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
@@ -208,7 +208,7 @@
return null;
}
- public ServletHandler getErrorsHandler(String requestURI, Long serviceId, int code, String exceptionType)
+ public ServletHandler getErrorsHandler(String requestURI, Long serviceId, int code, Throwable exception)
{
ErrorsMapping errorsMapping = getErrorsMapping(requestURI, serviceId);
if (errorsMapping == null)
@@ -216,14 +216,7 @@
return null;
}
- // TODO check exception hierarchy
- ServletHandler errorHandler = errorsMapping.get(exceptionType);
- if (errorHandler != null)
- {
- return errorHandler;
- }
-
- return errorsMapping.get(code);
+ return errorsMapping.get(exception, code);
}
private ErrorsMapping getErrorsMapping(final String requestURI, final Long serviceId)
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/ErrorPageTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/ErrorPageTest.java
new file mode 100644
index 0000000..dc06295
--- /dev/null
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/ErrorPageTest.java
@@ -0,0 +1,249 @@
+/*
+ * 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.felix.http.itest;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertTrue;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.context.ServletContextHelper;
+
+@RunWith(JUnit4TestRunner.class)
+public class ErrorPageTest extends BaseIntegrationTest
+{
+ private List<ServiceRegistration<?>> registrations = new ArrayList<ServiceRegistration<?>>();
+
+ private CountDownLatch initLatch;
+ private CountDownLatch destroyLatch;
+
+ public void setupLatches(int count)
+ {
+ initLatch = new CountDownLatch(count);
+ destroyLatch = new CountDownLatch(count);
+ }
+
+ public void setupErrorServlet(final Integer errorCode,
+ final Class<? extends RuntimeException> exceptionType,
+ String context) throws Exception
+ {
+ Dictionary<String, Object> servletProps = new Hashtable<String, Object>();
+ servletProps.put(HTTP_WHITEBOARD_SERVLET_NAME, "servlet");
+ servletProps.put(HTTP_WHITEBOARD_SERVLET_PATTERN, asList("/test"));
+ if (context != null)
+ {
+ servletProps.put(HTTP_WHITEBOARD_CONTEXT_SELECT, "(" + HTTP_WHITEBOARD_CONTEXT_NAME + "=" + context + ")");
+ }
+
+ TestServlet servletWithErrorCode = new TestServlet(initLatch, destroyLatch)
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws IOException
+ {
+ if (errorCode != null)
+ {
+ resp.sendError(errorCode);
+ }
+
+ if (exceptionType != null)
+ {
+ RuntimeException exception;
+ try
+ {
+ exception = exceptionType.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ throw exception;
+ }
+ }
+ };
+
+ registrations.add(m_context.registerService(Servlet.class.getName(), servletWithErrorCode, servletProps));
+ }
+
+ public void setupErrorPage(final Integer errorCode,
+ final Class<? extends Throwable> exceptionType,
+ final String name,
+ String context) throws Exception
+ {
+ TestServlet errorPage = new TestServlet(initLatch, destroyLatch)
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws IOException
+ {
+ resp.getWriter().print(name);
+ resp.flushBuffer();
+ }
+ };
+
+ List<String> errors = new ArrayList<String>();
+ if (errorCode != null)
+ {
+ errors.add(errorCode.toString());
+ }
+ if (exceptionType != null)
+ {
+ errors.add(exceptionType.getName());
+ }
+
+ Dictionary<String, Object> errorPageProps = new Hashtable<String, Object>();
+ errorPageProps.put(HTTP_WHITEBOARD_SERVLET_NAME, name);
+ errorPageProps.put(HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, errors);
+ if (context != null)
+ {
+ errorPageProps.put(HTTP_WHITEBOARD_CONTEXT_SELECT, "(" + HTTP_WHITEBOARD_CONTEXT_NAME + "=" + context + ")");
+ }
+
+ registrations.add(m_context.registerService(Servlet.class.getName(), errorPage, errorPageProps));
+ }
+
+ private void registerContext(String name, String path) throws InterruptedException
+ {
+ Dictionary<String, ?> properties = createDictionary(
+ HTTP_WHITEBOARD_CONTEXT_NAME, name,
+ HTTP_WHITEBOARD_CONTEXT_PATH, path);
+
+ ServletContextHelper servletContextHelper = new ServletContextHelper(m_context.getBundle()){
+ // test
+ };
+ registrations.add(m_context.registerService(ServletContextHelper.class.getName(), servletContextHelper, properties));
+
+ Thread.sleep(500);
+ }
+
+ @After
+ public void unregisterServices() throws InterruptedException
+ {
+ for (ServiceRegistration<?> serviceRegistration : registrations)
+ {
+ serviceRegistration.unregister();
+ }
+
+ assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
+
+ Thread.sleep(500);
+ }
+
+ @Test
+ public void errorPageForErrorCodeIsSent() throws Exception
+ {
+ setupLatches(2);
+ setupErrorServlet(501, null, null);
+ setupErrorPage(501, null, "Error page", null);
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+ assertContent(501, "Error page", createURL("/test"));
+ }
+
+ @Test
+ public void errorPageForExceptionIsSent() throws Exception
+ {
+ setupLatches(2);
+ setupErrorServlet(null, NullPointerException.class, null);
+ setupErrorPage(null, NullPointerException.class, "Error page", null);
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+ assertContent(500, "Error page", createURL("/test"));
+ }
+
+ @Test
+ public void errorPageForParentExceptionIsSent() throws Exception
+ {
+ setupLatches(2);
+ setupErrorServlet(null, NullPointerException.class, null);
+ setupErrorPage(null, RuntimeException.class, "Error page", null);
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+ assertContent(500, "Error page", createURL("/test"));
+ }
+
+ @Test
+ public void errorPageForExceptionIsPreferedOverErrorCode() throws Exception
+ {
+ setupLatches(3);
+ setupErrorServlet(null, NullPointerException.class, null);
+ setupErrorPage(500, null, "Error page 2", null);
+ setupErrorPage(null, NullPointerException.class, "Error page 1", null);
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+ assertContent(500, "Error page 1", createURL("/test"));
+ }
+
+ @Test
+ public void errorPageIsHandledPerContext() throws Exception
+ {
+ registerContext("context1", "/one");
+ registerContext("context2", "/two");
+
+ setupLatches(3);
+ setupErrorServlet(501, null, "context1");
+ setupErrorPage(501, null, "Error page 1", "context2");
+ setupErrorPage(501, null, "Error page 2", "context1");
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+ assertContent(501, "Error page 2", createURL("/one/test"));
+ }
+
+ @Test
+ public void errorPageIsShadowedByHigherRankingPage() throws Exception
+ {
+ registerContext("context1", "/one");
+ registerContext("context2", "/two");
+
+ // Shadowed error page is not initialized
+ setupLatches(2);
+ setupErrorServlet(501, null, "context1");
+ setupErrorPage(501, null, "Error page 1", "context1");
+ setupErrorPage(501, null, "Error page 2", "context1");
+ assertTrue(initLatch.await(5, TimeUnit.SECONDS));
+
+ assertContent(501, "Error page 1", createURL("/one/test"));
+ }
+}