Bug 460639 - better failure handling

Signed-off-by: Raymond Auge <raymond.auge@liferay.com>
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
index 351cd3f..67774ad 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
@@ -93,11 +93,15 @@
 
 		try {
 			if (contextName == null) {
-				throw new IllegalContextNameException(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + " is null. Ignoring!"); //$NON-NLS-1$
+				throw new IllegalContextNameException(
+					HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + " is null. Ignoring!", //$NON-NLS-1$
+					DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
 			}
 
 			if (contextPath == null) {
-				throw new IllegalContextPathException(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + " is null. Ignoring!"); //$NON-NLS-1$
+				throw new IllegalContextPathException(
+					HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + " is null. Ignoring!", //$NON-NLS-1$
+					DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
 			}
 
 			contextPath = adaptContextPath(contextPath, serviceReference);
@@ -110,6 +114,11 @@
 
 			result.set(contextController);
 		}
+		catch (HttpWhiteboardFailureException hwfe) {
+			parentServletContext.log(hwfe.getMessage(), hwfe);
+
+			recordFailedServletContextDTO(serviceReference, contextName, contextPath, hwfe.getFailureReason());
+		}
 		catch (Exception e) {
 			parentServletContext.log(e.getMessage(), e);
 
@@ -166,6 +175,7 @@
 		controllerMap.clear();
 		registeredObjects.clear();
 
+		failedListenerDTOs.clear();
 		failedServletContextDTOs.clear();
 
 		attributes = null;
@@ -206,7 +216,7 @@
 
 		runtimeDTO.failedErrorPageDTOs = null;
 		runtimeDTO.failedFilterDTOs = null;
-		runtimeDTO.failedListenerDTOs = null;
+		runtimeDTO.failedListenerDTOs = getFailedListenerDTOs();
 		runtimeDTO.failedResourceDTOs = null;
 		runtimeDTO.failedServletContextDTOs = getFailedServletContextDTO();
 		runtimeDTO.failedServletDTOs = null;
@@ -451,6 +461,18 @@
 		return null;
 	}
 
+	private FailedListenerDTO[] getFailedListenerDTOs() {
+		Collection<FailedListenerDTO> flDTOs = failedListenerDTOs.values();
+
+		List<FailedListenerDTO> copies = new ArrayList<FailedListenerDTO>();
+
+		for (FailedListenerDTO failedListenerDTO : flDTOs) {
+			copies.add(DTOUtil.clone(failedListenerDTO));
+		}
+
+		return copies.toArray(new FailedListenerDTO[copies.size()]);
+	}
+
 	private FailedServletContextDTO[] getFailedServletContextDTO() {
 		Collection<FailedServletContextDTO> fscDTOs = failedServletContextDTOs.values();
 
@@ -933,6 +955,17 @@
 		return resourceServiceFilter;
 	}
 
+	public void recordFailedListenerDTO(
+		ServiceReference<EventListener> serviceReference,
+		FailedListenerDTO failedListenerDTO) {
+
+		if (failedListenerDTOs.containsKey(serviceReference)) {
+			return;
+		}
+
+		failedListenerDTOs.put(serviceReference, failedListenerDTO);
+	}
+
 	private void recordFailedServletContextDTO(
 		ServiceReference<ServletContextHelper> serviceReference, String contextName,
 		String contextPath, int failureReason) {
@@ -956,6 +989,12 @@
 		failedServletContextDTOs.put(serviceReference, failedServletContextDTO);
 	}
 
+	public void removeFailedListenerDTO(
+		ServiceReference<EventListener> serviceReference) {
+
+		failedListenerDTOs.remove(serviceReference);
+	}
+
 	private Map<String, Object> attributes;
 	private final String targetFilter;
 	private final ServiceRegistration<ServletContextHelper> defaultContextReg;
@@ -982,6 +1021,8 @@
 	private ConcurrentMap<ServiceReference<ServletContextHelper>, ContextController> controllerMap =
 		new ConcurrentHashMap<ServiceReference<ServletContextHelper>, ContextController>();
 
+	private final ConcurrentMap<ServiceReference<EventListener>, FailedListenerDTO> failedListenerDTOs =
+		new ConcurrentHashMap<ServiceReference<EventListener>, FailedListenerDTO>();
 	private final ConcurrentMap<ServiceReference<ServletContextHelper>, FailedServletContextDTO> failedServletContextDTOs =
 		new ConcurrentHashMap<ServiceReference<ServletContextHelper>, FailedServletContextDTO>();
 
@@ -1199,4 +1240,5 @@
 			}
 		}
 	}
+
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
index 5b2b585..bb78547 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
@@ -1078,7 +1078,9 @@
 
 	private void validate(String preValidationContextName, String preValidationContextPath) {
 		if (!contextNamePattern.matcher(preValidationContextName).matches()) {
-			throw new IllegalContextNameException("The context name '" + preValidationContextName + "' does not follow Bundle-SymbolicName syntax."); //$NON-NLS-1$ //$NON-NLS-2$
+			throw new IllegalContextNameException(
+				"The context name '" + preValidationContextName + "' does not follow Bundle-SymbolicName syntax.", //$NON-NLS-1$ //$NON-NLS-2$
+				DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
 		}
 
 		try {
@@ -1086,7 +1088,9 @@
 			URI uri = new URI(Const.HTTP, Const.LOCALHOST, preValidationContextPath, null);
 		}
 		catch (URISyntaxException use) {
-			throw new IllegalContextPathException("The context path '" + preValidationContextPath + "' is not valid URI path syntax."); //$NON-NLS-1$ //$NON-NLS-2$
+			throw new IllegalContextPathException(
+				"The context path '" + preValidationContextPath + "' is not valid URI path syntax.", //$NON-NLS-1$ //$NON-NLS-2$
+				DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
 		}
 	}
 
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextListenerTrackerCustomizer.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextListenerTrackerCustomizer.java
index 6f1a254..5d179ae 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextListenerTrackerCustomizer.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextListenerTrackerCustomizer.java
@@ -13,12 +13,15 @@
 
 import java.util.EventListener;
 import java.util.concurrent.atomic.AtomicReference;
-import javax.servlet.ServletException;
 import org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
+import org.eclipse.equinox.http.servlet.internal.error.HttpWhiteboardFailureException;
 import org.eclipse.equinox.http.servlet.internal.registration.ListenerRegistration;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
+import org.eclipse.equinox.http.servlet.internal.util.Const;
+import org.osgi.framework.*;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedListenerDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 
 /**
  * @author Raymond Augé
@@ -49,12 +52,26 @@
 			return result;
 		}
 
+		String listener = (String)serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER);
 
 		try {
+			if (!listener.equalsIgnoreCase(Const.TRUE)) {
+				throw new HttpWhiteboardFailureException(
+					HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER + "=" + listener + " is not a valid option. Ignoring!", //$NON-NLS-1$ //$NON-NLS-2$
+					DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
+			}
+
 			result.set(contextController.addListenerRegistration(serviceReference));
 		}
-		catch (ServletException se) {
-			httpServiceRuntime.log(se.getMessage(), se);
+		catch (HttpWhiteboardFailureException hwfe) {
+			httpServiceRuntime.log(hwfe.getMessage(), hwfe);
+
+			recordFailedListenerDTO(serviceReference, hwfe.getFailureReason());
+		}
+		catch (Exception e) {
+			httpServiceRuntime.log(e.getMessage(), e);
+
+			recordFailedListenerDTO(serviceReference, DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT);
 		}
 
 		return result;
@@ -80,6 +97,21 @@
 			// Destroy now ungets the object we are using
 			listenerRegistration.destroy();
 		}
+
+		contextController.getHttpServiceRuntime().removeFailedListenerDTO(serviceReference);
+	}
+
+	private void recordFailedListenerDTO(
+		ServiceReference<EventListener> serviceReference, int failureReason) {
+
+		FailedListenerDTO failedListenerDTO = new FailedListenerDTO();
+
+		failedListenerDTO.failureReason = failureReason;
+		failedListenerDTO.serviceId = (Long)serviceReference.getProperty(Constants.SERVICE_ID);
+		failedListenerDTO.servletContextId = contextController.getServiceId();
+		failedListenerDTO.types = new String[0];
+
+		contextController.getHttpServiceRuntime().recordFailedListenerDTO(serviceReference, failedListenerDTO);
 	}
 
 	private ContextController contextController;
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/HttpWhiteboardFailureException.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/HttpWhiteboardFailureException.java
new file mode 100644
index 0000000..47ba32f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/HttpWhiteboardFailureException.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) Feb 23, 2015 Raymond Augé and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Raymond Augé <raymond.auge@liferay.com> - Bug 460639
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.internal.error;
+
+/**
+ * @author Raymond Augé
+ */
+public class HttpWhiteboardFailureException extends IllegalArgumentException {
+
+	private static final long serialVersionUID = 1944632136470074075L;
+
+	public HttpWhiteboardFailureException(String message, int failureReason) {
+		super(message);
+
+		this.failureReason = failureReason;
+	}
+
+	public int getFailureReason() {
+		return failureReason;
+	}
+
+	private final int failureReason;
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/IllegalContextNameException.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/IllegalContextNameException.java
index 7e6f183..c0c4a1b 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/IllegalContextNameException.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/IllegalContextNameException.java
@@ -14,12 +14,12 @@
 /**
  * @author Raymond Augé
  */
-public class IllegalContextNameException extends IllegalArgumentException {
+public class IllegalContextNameException extends HttpWhiteboardFailureException {
 
 	private static final long serialVersionUID = -8790109985246626513L;
 
-	public IllegalContextNameException(String message) {
-		super(message);
+	public IllegalContextNameException(String message, int failureReason) {
+		super(message, failureReason);
 	}
 
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/IllegalContextPathException.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/IllegalContextPathException.java
index 1f8244e..8b8410a 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/IllegalContextPathException.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/IllegalContextPathException.java
@@ -14,12 +14,12 @@
 /**
  * @author Raymond Augé
  */
-public class IllegalContextPathException extends IllegalArgumentException {
+public class IllegalContextPathException extends HttpWhiteboardFailureException {
 
 	private static final long serialVersionUID = 3286236189163243168L;
 
-	public IllegalContextPathException(String message) {
-		super(message);
+	public IllegalContextPathException(String message, int failureReason) {
+		super(message, failureReason);
 	}
 
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
index 8545e4a..63529b9 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
@@ -28,6 +28,7 @@
 	public static final String SERVLET_NAME = "servlet-name"; //$NON-NLS-1$
 	public static final String SLASH = "/"; //$NON-NLS-1$
 	public static final String SLASH_STAR = "/*"; //$NON-NLS-1$
+	public static final String TRUE = "true"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_REGISTRATION_PROP = "equinox.legacy.registration"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_CONTEXT_SELECT = "equinox.context.select"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_CONTEXT_HELPER = "equinox.legacy.context.helper"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
index 68b7eb8..adea45e 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
@@ -33,6 +33,17 @@
 		return clone;
 	}
 
+	public static FailedListenerDTO clone(FailedListenerDTO original) {
+		FailedListenerDTO clone = new FailedListenerDTO();
+
+		clone.failureReason = original.failureReason;
+		clone.serviceId = original.serviceId;
+		clone.servletContextId = original.servletContextId;
+		clone.types = original.types;
+
+		return clone;
+	}
+
 	public static FailedServletContextDTO clone(FailedServletContextDTO original) {
 		FailedServletContextDTO clone = new FailedServletContextDTO();