FELIX-4780 : [RFC189] Revisit error page implementation
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1656459 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/DispatcherServlet.java b/http/base/src/main/java/org/apache/felix/http/base/internal/DispatcherServlet.java
index 0bca9bc..a8b3188 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/DispatcherServlet.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/DispatcherServlet.java
@@ -54,11 +54,6 @@
super.destroy();
}
- public boolean handleError(HttpServletRequest request, HttpServletResponse response, int errorCode, String exceptionType) throws IOException
- {
- return this.controller.getDispatcher().handleError(request, response, errorCode, exceptionType);
- }
-
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
index af79bb4..748db74 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
@@ -24,6 +24,7 @@
import org.apache.felix.http.base.internal.dispatch.Dispatcher;
import org.apache.felix.http.base.internal.handler.HandlerRegistry;
import org.apache.felix.http.base.internal.handler.HttpServicePlugin;
+import org.apache.felix.http.base.internal.handler.PerContextHandlerRegistry;
import org.apache.felix.http.base.internal.listener.HttpSessionAttributeListenerManager;
import org.apache.felix.http.base.internal.listener.HttpSessionListenerManager;
import org.apache.felix.http.base.internal.listener.ServletContextAttributeListenerManager;
@@ -78,6 +79,7 @@
{
this.bundleContext = bundleContext;
this.registry = new HandlerRegistry();
+ this.registry.add(new PerContextHandlerRegistry());
this.dispatcher = new Dispatcher(this.registry);
this.serviceProps = new Hashtable<String, Object>();
this.contextAttributeListener = new ServletContextAttributeListenerManager(bundleContext);
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 17922fb..bf5ead1 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
@@ -31,6 +31,7 @@
import static org.apache.felix.http.base.internal.util.UriUtils.removeDotSegments;
import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.DispatcherType;
import javax.servlet.FilterChain;
@@ -42,6 +43,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import org.apache.felix.http.base.internal.context.ExtServletContext;
@@ -108,6 +110,125 @@
}
}
+ final class ServletResponseWrapper extends HttpServletResponseWrapper
+ {
+
+ private final HttpServletRequest request;
+
+ private final AtomicInteger invocationCount = new AtomicInteger();
+
+ private final Long serviceId;
+
+ private final String servletName;
+
+ public ServletResponseWrapper(final HttpServletRequest req, final HttpServletResponse res, final ServletHandler servletHandler)
+ {
+ super(res);
+ this.request = req;
+ if ( servletHandler != null )
+ {
+ this.serviceId = servletHandler.getContextServiceId();
+ this.servletName = servletHandler.getName();
+ }
+ else
+ {
+ this.serviceId = null;
+ this.servletName = null;
+ }
+ }
+
+ @Override
+ public void sendError(int sc) throws IOException
+ {
+ sendError(sc, null);
+ }
+
+ @Override
+ public void sendError(final int code, final String message) throws IOException
+ {
+ resetBuffer();
+
+ setStatus(code);
+
+ boolean invokeSuper = true;
+
+ if ( invocationCount.incrementAndGet() == 1 )
+ {
+ // If we are allowed to have a body
+ if (code != SC_NO_CONTENT &&
+ code != SC_NOT_MODIFIED &&
+ code != SC_PARTIAL_CONTENT &&
+ code >= SC_OK)
+ {
+
+ final ErrorsMapping errorsMapping = handlerRegistry.getErrorsMapping(request.getRequestURI(), this.serviceId);
+ if ( errorsMapping != null )
+ {
+ final String exceptionType = (String)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
+
+ ServletHandler errorHandler = null;
+
+ if (exceptionType != null)
+ {
+ errorHandler = errorsMapping.get(exceptionType);
+ }
+
+ if ( errorHandler == null )
+ {
+ errorHandler = errorsMapping.get(code);
+ }
+
+ if ( errorHandler != null )
+ {
+ try
+ {
+ request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(code));
+ if ( message != null )
+ {
+ request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
+ }
+ request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
+ if ( this.servletName != null )
+ {
+ request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, this.servletName);
+ }
+
+ final String servletPath = null;
+ final String pathInfo = request.getRequestURI();
+ final String queryString = null; // XXX
+
+ final RequestInfo requestInfo = new RequestInfo(servletPath, pathInfo, queryString);
+
+ final FilterHandler[] filterHandlers = handlerRegistry.getFilterHandlers(errorHandler, DispatcherType.ERROR, request.getRequestURI());
+
+ invokeChain(filterHandlers, errorHandler, new ServletRequestWrapper(request, errorHandler.getContext(), requestInfo), this);
+
+ invokeSuper = false;
+ }
+ catch (final ServletException e)
+ {
+ // ignore
+ }
+ finally
+ {
+ request.removeAttribute(RequestDispatcher.ERROR_STATUS_CODE);
+ request.removeAttribute(RequestDispatcher.ERROR_MESSAGE);
+ request.removeAttribute(RequestDispatcher.ERROR_REQUEST_URI);
+ request.removeAttribute(RequestDispatcher.ERROR_SERVLET_NAME);
+ request.removeAttribute(RequestDispatcher.ERROR_EXCEPTION);
+ request.removeAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
+ }
+ }
+ }
+ }
+ }
+ if ( invokeSuper )
+ {
+ super.sendError(code, message);
+ }
+ }
+ }
+
final class ServletRequestWrapper extends HttpServletRequestWrapper
{
private final DispatcherType type;
@@ -403,11 +524,17 @@
*/
public void dispatch(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
- String requestURI = getRequestURI(req);
+ final String requestURI = getRequestURI(req);
// Determine which servlets we should forward the request to...
- ServletHandler servletHandler = this.handlerRegistry.getServletHander(requestURI);
- FilterHandler[] filterHandlers = this.handlerRegistry.getFilterHandlers(servletHandler, req.getDispatcherType(), requestURI);
+ final ServletHandler servletHandler = this.handlerRegistry.getServletHander(requestURI);
+
+ final HttpServletResponse wrappedResponse = new ServletResponseWrapper(req, res, servletHandler);
+ if ( servletHandler == null )
+ {
+ wrappedResponse.sendError(404);
+ return;
+ }
// Provides access to the correct request dispatcher...
req.setAttribute(REQUEST_DISPATCHER_PROVIDER, this);
@@ -421,7 +548,16 @@
try
{
- invokeChain(filterHandlers, servletHandler, new ServletRequestWrapper(req, servletContext, requestInfo), res);
+ final HttpServletRequest wrappedRequest = new ServletRequestWrapper(req, servletContext, requestInfo);
+ final FilterHandler[] filterHandlers = this.handlerRegistry.getFilterHandlers(servletHandler, req.getDispatcherType(), requestURI);
+ invokeChain(filterHandlers, servletHandler, wrappedRequest, wrappedResponse);
+ }
+ catch ( final Exception e)
+ {
+ req.setAttribute(RequestDispatcher.ERROR_EXCEPTION, e);
+ req.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, e.getClass().getName());
+
+ wrappedResponse.sendError(500);
}
finally
{
@@ -429,36 +565,6 @@
}
}
- public boolean handleError(HttpServletRequest request, HttpServletResponse response, int errorCode, String exceptionType) throws IOException
- {
- ErrorsMapping errorsMapping = this.handlerRegistry.getErrorsMapping();
- ServletHandler errorHandler = null;
-
- if (exceptionType != null)
- {
- errorHandler = errorsMapping.get(exceptionType);
- }
- else
- {
- errorHandler = errorsMapping.get(errorCode);
- }
-
- if (errorHandler != null)
- {
- // TODO set error attributes! See Servlet 3.0 specification, section 10.9.1...
- try
- {
- return errorHandler.handle(request, response);
- }
- catch (ServletException e)
- {
- return false; // XXX
- }
- }
-
- return false;
- }
-
@Override
public RequestDispatcher getNamedDispatcher(String name)
{
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 96b5627..62a2e9d 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
@@ -17,280 +17,148 @@
package org.apache.felix.http.base.internal.handler;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
+import javax.annotation.Nonnull;
import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
-import javax.servlet.Servlet;
-import javax.servlet.ServletException;
import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
-import org.apache.felix.http.base.internal.runtime.FilterInfo;
-import org.apache.felix.http.base.internal.runtime.ServletInfo;
-import org.osgi.service.http.NamespaceException;
public final class HandlerRegistry
{
- private final Map<Servlet, ServletHandler> servletMap = new HashMap<Servlet, ServletHandler>();
- private final Map<Filter, FilterHandler> filterMap = new HashMap<Filter, FilterHandler>();
- private final Map<String, Servlet> servletPatternMap= new HashMap<String, Servlet>();
- private volatile HandlerMapping<ServletHandler> servletMapping = new HandlerMapping<ServletHandler>();
- private volatile HandlerMapping<FilterHandler> filterMapping = new HandlerMapping<FilterHandler>();
- private volatile ErrorsMapping errorsMapping = new ErrorsMapping();
+ private volatile List<PerContextHandlerRegistry> registrations = Collections.emptyList();
- public synchronized void addFilter(FilterHandler handler) throws ServletException
+ public void add(@Nonnull PerContextHandlerRegistry registry)
{
- if (this.filterMap.containsKey(handler.getFilter()))
+ synchronized ( this )
{
- throw new ServletException("Filter instance already registered");
+ final List<PerContextHandlerRegistry> updatedList = new ArrayList<PerContextHandlerRegistry>(this.registrations);
+ updatedList.add(registry);
+ Collections.sort(updatedList);
+
+ this.registrations = updatedList;
}
-
- handler.init();
- this.filterMap.put(handler.getFilter(), handler);
-
- updateFilterMapping();
}
- /**
- * Add a new servlet.
- */
- public synchronized void addServlet(ServletContextHelperInfo contextInfo, ServletHandler handler) throws ServletException, NamespaceException
+ public void remove(@Nonnull PerContextHandlerRegistry registry)
{
- if (this.servletMap.containsKey(handler.getServlet()))
+ synchronized ( this )
{
- // Do not destroy the servlet as the same instance was already registered
- throw new ServletException("Servlet instance " + handler.getName() + " already registered");
+ final List<PerContextHandlerRegistry> updatedList = new ArrayList<PerContextHandlerRegistry>(this.registrations);
+ updatedList.remove(registry);
+
+ this.registrations = updatedList;
}
-
- // Can be null in case of error-handling servlets...
- String[] patterns = handler.getServletInfo().getPatterns();
- int length = patterns == null ? 0 : patterns.length;
-
- for (int i = 0; i < length; i++)
- {
- String pattern = contextInfo != null ? contextInfo.getFullPath(patterns[i]) : patterns[i];
- if (this.servletPatternMap.containsKey(pattern))
- {
- throw new ServletException("Servlet instance " + handler.getName() + " already registered");
- }
- this.servletPatternMap.put(pattern, handler.getServlet());
- }
-
- patterns = handler.getServletInfo().getErrorPage();
- if ( patterns != null )
- {
- for(final String errorPage : patterns)
- {
- this.errorsMapping.addErrorServlet(errorPage, handler);
- }
- }
- handler.init();
- this.servletMap.put(handler.getServlet(), handler);
-
- updateServletMapping();
}
- public ErrorsMapping getErrorsMapping()
+ public PerContextHandlerRegistry getRegistry(final ServletContextHelperInfo info)
{
- return this.errorsMapping;
+ synchronized ( this )
+ {
+ for(final PerContextHandlerRegistry r : this.registrations)
+ {
+ if ( info == null )
+ {
+ if ( r.getContextServiceid() == -1)
+ {
+ return r;
+ }
+ }
+ else
+ {
+ if ( info.getServiceId() == r.getContextServiceid())
+ {
+ return r;
+ }
+ }
+ }
+ final PerContextHandlerRegistry reg = new PerContextHandlerRegistry(info);
+ this.add(reg);
+
+ return reg;
+ }
}
- public FilterHandler[] getFilterHandlers(ServletHandler servletHandler, DispatcherType dispatcherType, String requestURI)
+ public ErrorsMapping getErrorsMapping(final String requestURI, final Long serviceId)
{
- // See Servlet 3.0 specification, section 6.2.4...
- List<FilterHandler> result = new ArrayList<FilterHandler>();
- result.addAll(this.filterMapping.getAllMatches(requestURI));
-
- // TODO this is not the most efficient/fastest way of doing this...
- Iterator<FilterHandler> iter = result.iterator();
- while (iter.hasNext())
+ final List<PerContextHandlerRegistry> regs = this.registrations;
+ for(final PerContextHandlerRegistry r : regs)
{
- if (!referencesDispatcherType(iter.next(), dispatcherType))
+ if ( serviceId != null && serviceId == r.getContextServiceid() )
{
- iter.remove();
+ return r.getErrorsMapping();
+ }
+ else if ( serviceId == null && requestURI.startsWith(r.getPrefixPath()) )
+ {
+ return r.getErrorsMapping();
}
}
- String servletName = (servletHandler != null) ? servletHandler.getName() : null;
- // TODO this is not the most efficient/fastest way of doing this...
- for (FilterHandler filterHandler : this.filterMapping.getAllElements())
- {
- if (referencesServletByName(filterHandler, servletName))
- {
- result.add(filterHandler);
- }
- }
-
- // TODO - we should already check for the context when building up the result set
- final Iterator<FilterHandler> i = result.iterator();
- while ( i.hasNext() )
- {
- final FilterHandler handler = i.next();
- if ( handler.getContextServiceId() != servletHandler.getContextServiceId() )
- {
- i.remove();
- }
- }
- return result.toArray(new FilterHandler[result.size()]);
+ return null;
}
+ private static FilterHandler[] EMPTY_FILTER_HANDLER = new FilterHandler[0];
+
+ public FilterHandler[] getFilterHandlers(final ServletHandler servletHandler,
+ final DispatcherType dispatcherType,
+ final String requestURI)
+ {
+ final long id = servletHandler.getContextServiceId();
+ final List<PerContextHandlerRegistry> regs = this.registrations;
+ for(final PerContextHandlerRegistry r : regs)
+ {
+ if ( id == r.getContextServiceid() )
+ {
+ return r.getFilterHandlers(servletHandler, dispatcherType, requestURI);
+ }
+ }
+ return EMPTY_FILTER_HANDLER;
+ }
+/*
public synchronized Servlet getServletByAlias(String alias)
{
return this.servletPatternMap.get(alias);
}
-
- public ServletHandler getServletHandlerByName(String name)
+*/
+ public ServletHandler getServletHandlerByName(final String name)
{
- return this.servletMapping.getByName(name);
+ // TODO
+ return null;
}
- public ServletHandler getServletHander(String requestURI)
+ public ServletHandler getServletHander(final String requestURI)
{
- // TODO - take servlet context helper ranking and prefix into account (FELIX-4778)
- return this.servletMapping.getBestMatch(requestURI);
+ final List<PerContextHandlerRegistry> regs = this.registrations;
+ for(final PerContextHandlerRegistry r : regs)
+ {
+ if ( requestURI.startsWith(r.getPrefixPath()))
+ {
+ // TODO - we could stip of the prefix and simplify handler registration
+ final ServletHandler handler = r.getServletHander(requestURI);
+ if ( handler != null )
+ {
+ return handler;
+ }
+ }
+ }
+ return null;
}
public synchronized void removeAll()
{
- for (Iterator<ServletHandler> it = servletMap.values().iterator(); it.hasNext(); )
+ final List<PerContextHandlerRegistry> list;
+
+ synchronized ( this )
{
- ServletHandler handler = it.next();
- it.remove();
- handler.destroy();
+ list = new ArrayList<PerContextHandlerRegistry>(this.registrations);
+ this.registrations = Collections.emptyList();
+
}
- for (Iterator<FilterHandler> it = filterMap.values().iterator(); it.hasNext(); )
+ for(final PerContextHandlerRegistry r : list)
{
- FilterHandler handler = it.next();
- it.remove();
- handler.destroy();
+ r.removeAll();
}
-
- this.servletMap.clear();
- this.filterMap.clear();
- this.servletPatternMap.clear();
- this.errorsMapping.clear();
-
- updateServletMapping();
- updateFilterMapping();
- }
-
- public synchronized void removeFilter(Filter filter, final boolean destroy)
- {
- FilterHandler handler = this.filterMap.remove(filter);
- if (handler != null)
- {
- updateFilterMapping();
- if (destroy)
- {
- handler.destroy();
- }
- }
- }
-
- public synchronized Filter removeFilter(final FilterInfo filterInfo, final boolean destroy)
- {
- for(final FilterHandler handler : this.filterMap.values())
- {
- if ( handler.getFilterInfo().compareTo(filterInfo) == 0)
- {
- this.filterMap.remove(handler.getFilter());
- updateFilterMapping();
- if (destroy)
- {
- handler.destroy();
- }
- return handler.getFilter();
- }
- }
- return null;
- }
-
- public synchronized Servlet removeServlet(final ServletContextHelperInfo contextInfo, ServletInfo servletInfo, final boolean destroy)
- {
- for(final ServletHandler handler : this.servletMap.values())
- {
- if ( handler.getServletInfo().compareTo(servletInfo) == 0 )
- {
- this.servletMap.remove(handler.getServlet());
- updateServletMapping();
-
- // Can be null in case of error-handling servlets...
- String[] patterns = handler.getServletInfo().getPatterns();
- int length = patterns == null ? 0 : patterns.length;
-
- for (int i = 0; i < length; i++)
- {
- this.servletPatternMap.remove(contextInfo.getFullPath(patterns[i]));
- }
-
- this.errorsMapping.removeServlet(handler.getServlet());
-
- if (destroy)
- {
- handler.destroy();
- }
- return handler.getServlet();
- }
- }
- return null;
- }
-
- public synchronized void removeServlet(Servlet servlet, final boolean destroy)
- {
- ServletHandler handler = this.servletMap.remove(servlet);
- if (handler != null)
- {
- updateServletMapping();
-
- // Can be null in case of error-handling servlets...
- String[] patterns = handler.getServletInfo().getPatterns();
- int length = patterns == null ? 0 : patterns.length;
-
- for (int i = 0; i < length; i++)
- {
- this.servletPatternMap.remove(patterns[i]);
- }
-
- this.errorsMapping.removeServlet(servlet);
-
- if (destroy)
- {
- handler.destroy();
- }
- }
- }
-
- private boolean referencesDispatcherType(FilterHandler handler, DispatcherType dispatcherType)
- {
- return Arrays.asList(handler.getFilterInfo().getDispatcher()).contains(dispatcherType);
- }
-
- private boolean referencesServletByName(FilterHandler handler, String servletName)
- {
- if (servletName == null)
- {
- return false;
- }
- String[] names = handler.getFilterInfo().getServletNames();
- if (names != null && names.length > 0)
- {
- return Arrays.asList(names).contains(servletName);
- }
- return false;
- }
-
- private void updateFilterMapping()
- {
- this.filterMapping = new HandlerMapping<FilterHandler>(this.filterMap.values());
- }
-
- private void updateServletMapping()
- {
- this.servletMapping = new HandlerMapping<ServletHandler>(this.servletMap.values());
}
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
new file mode 100644
index 0000000..c1e93e7
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
@@ -0,0 +1,353 @@
+/*
+ * 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.base.internal.handler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.osgi.service.http.NamespaceException;
+
+public final class PerContextHandlerRegistry implements Comparable<PerContextHandlerRegistry>
+{
+ private final Map<Servlet, ServletHandler> servletMap = new HashMap<Servlet, ServletHandler>();
+ private final Map<Filter, FilterHandler> filterMap = new HashMap<Filter, FilterHandler>();
+ private final Map<String, Servlet> servletPatternMap= new HashMap<String, Servlet>();
+ private volatile HandlerMapping<ServletHandler> servletMapping = new HandlerMapping<ServletHandler>();
+ private volatile HandlerMapping<FilterHandler> filterMapping = new HandlerMapping<FilterHandler>();
+ private volatile ErrorsMapping errorsMapping = new ErrorsMapping();
+
+ private final long serviceId;
+
+ private final int ranking;
+
+ private final String prefixPath;
+
+ public PerContextHandlerRegistry() {
+ this.serviceId = -1;
+ this.ranking = Integer.MIN_VALUE;
+ this.prefixPath = "/";
+ }
+
+ public PerContextHandlerRegistry(final ServletContextHelperInfo info)
+ {
+ this.serviceId = info.getServiceId();
+ this.ranking = info.getRanking();
+ this.prefixPath = info.getPath();
+ }
+
+ public synchronized void addFilter(FilterHandler handler) throws ServletException
+ {
+ if (this.filterMap.containsKey(handler.getFilter()))
+ {
+ throw new ServletException("Filter instance already registered");
+ }
+
+ handler.init();
+ this.filterMap.put(handler.getFilter(), handler);
+
+ updateFilterMapping();
+ }
+
+ @Override
+ public int compareTo(final PerContextHandlerRegistry other)
+ {
+ if (other.ranking == this.ranking)
+ {
+ if (other.serviceId == this.serviceId)
+ {
+ return 0;
+ }
+ // service id might be negative, we have to change the behavior in that case
+ if ( this.serviceId < 0 )
+ {
+ if ( other.serviceId > 0 )
+ {
+ return -1;
+ }
+ return other.serviceId < this.serviceId ? -1 : 1;
+ }
+ if ( other.serviceId < 0 )
+ {
+ return -1;
+ }
+ return other.serviceId > this.serviceId ? -1 : 1;
+ }
+
+ return (other.ranking > this.ranking) ? 1 : -1;
+ }
+
+ /**
+ * Add a new servlet.
+ */
+ public synchronized void addServlet(ServletContextHelperInfo contextInfo, ServletHandler handler) throws ServletException, NamespaceException
+ {
+ if (this.servletMap.containsKey(handler.getServlet()))
+ {
+ // Do not destroy the servlet as the same instance was already registered
+ throw new ServletException("Servlet instance " + handler.getName() + " already registered");
+ }
+
+ // Can be null in case of error-handling servlets...
+ String[] patterns = handler.getServletInfo().getPatterns();
+ int length = patterns == null ? 0 : patterns.length;
+
+ for (int i = 0; i < length; i++)
+ {
+ String pattern = contextInfo != null ? contextInfo.getFullPath(patterns[i]) : patterns[i];
+ if (this.servletPatternMap.containsKey(pattern))
+ {
+ throw new ServletException("Servlet instance " + handler.getName() + " already registered");
+ }
+ this.servletPatternMap.put(pattern, handler.getServlet());
+ }
+
+ patterns = handler.getServletInfo().getErrorPage();
+ if ( patterns != null )
+ {
+ for(final String errorPage : patterns)
+ {
+ this.errorsMapping.addErrorServlet(errorPage, handler);
+ }
+ }
+ handler.init();
+ this.servletMap.put(handler.getServlet(), handler);
+
+ updateServletMapping();
+ }
+
+ public ErrorsMapping getErrorsMapping()
+ {
+ return this.errorsMapping;
+ }
+
+ public FilterHandler[] getFilterHandlers(ServletHandler servletHandler, DispatcherType dispatcherType, String requestURI)
+ {
+ // See Servlet 3.0 specification, section 6.2.4...
+ List<FilterHandler> result = new ArrayList<FilterHandler>();
+ result.addAll(this.filterMapping.getAllMatches(requestURI));
+
+ // TODO this is not the most efficient/fastest way of doing this...
+ Iterator<FilterHandler> iter = result.iterator();
+ while (iter.hasNext())
+ {
+ if (!referencesDispatcherType(iter.next(), dispatcherType))
+ {
+ iter.remove();
+ }
+ }
+
+ String servletName = (servletHandler != null) ? servletHandler.getName() : null;
+ // TODO this is not the most efficient/fastest way of doing this...
+ for (FilterHandler filterHandler : this.filterMapping.getAllElements())
+ {
+ if (referencesServletByName(filterHandler, servletName))
+ {
+ result.add(filterHandler);
+ }
+ }
+
+ // TODO - we should already check for the context when building up the result set
+ final Iterator<FilterHandler> i = result.iterator();
+ while ( i.hasNext() )
+ {
+ final FilterHandler handler = i.next();
+ if ( handler.getContextServiceId() != servletHandler.getContextServiceId() )
+ {
+ i.remove();
+ }
+ }
+ return result.toArray(new FilterHandler[result.size()]);
+ }
+
+ public synchronized Servlet getServletByAlias(String alias)
+ {
+ return this.servletPatternMap.get(alias);
+ }
+
+ public ServletHandler getServletHandlerByName(String name)
+ {
+ return this.servletMapping.getByName(name);
+ }
+
+ public ServletHandler getServletHander(String requestURI)
+ {
+ // TODO - take servlet context helper ranking and prefix into account (FELIX-4778)
+ return this.servletMapping.getBestMatch(requestURI);
+ }
+
+ public synchronized void removeAll()
+ {
+ for (Iterator<ServletHandler> it = servletMap.values().iterator(); it.hasNext(); )
+ {
+ ServletHandler handler = it.next();
+ it.remove();
+ handler.destroy();
+ }
+
+ for (Iterator<FilterHandler> it = filterMap.values().iterator(); it.hasNext(); )
+ {
+ FilterHandler handler = it.next();
+ it.remove();
+ handler.destroy();
+ }
+
+ this.servletMap.clear();
+ this.filterMap.clear();
+ this.servletPatternMap.clear();
+ this.errorsMapping.clear();
+
+ updateServletMapping();
+ updateFilterMapping();
+ }
+
+ public synchronized void removeFilter(Filter filter, final boolean destroy)
+ {
+ FilterHandler handler = this.filterMap.remove(filter);
+ if (handler != null)
+ {
+ updateFilterMapping();
+ if (destroy)
+ {
+ handler.destroy();
+ }
+ }
+ }
+
+ public synchronized Filter removeFilter(final FilterInfo filterInfo, final boolean destroy)
+ {
+ for(final FilterHandler handler : this.filterMap.values())
+ {
+ if ( handler.getFilterInfo().compareTo(filterInfo) == 0)
+ {
+ this.filterMap.remove(handler.getFilter());
+ updateFilterMapping();
+ if (destroy)
+ {
+ handler.destroy();
+ }
+ return handler.getFilter();
+ }
+ }
+ return null;
+ }
+
+ public synchronized Servlet removeServlet(final ServletContextHelperInfo contextInfo, ServletInfo servletInfo, final boolean destroy)
+ {
+ for(final ServletHandler handler : this.servletMap.values())
+ {
+ if ( handler.getServletInfo().compareTo(servletInfo) == 0 )
+ {
+ this.servletMap.remove(handler.getServlet());
+ updateServletMapping();
+
+ // Can be null in case of error-handling servlets...
+ String[] patterns = handler.getServletInfo().getPatterns();
+ int length = patterns == null ? 0 : patterns.length;
+
+ for (int i = 0; i < length; i++)
+ {
+ this.servletPatternMap.remove(contextInfo.getFullPath(patterns[i]));
+ }
+
+ this.errorsMapping.removeServlet(handler.getServlet());
+
+ if (destroy)
+ {
+ handler.destroy();
+ }
+ return handler.getServlet();
+ }
+ }
+ return null;
+ }
+
+ public synchronized void removeServlet(Servlet servlet, final boolean destroy)
+ {
+ ServletHandler handler = this.servletMap.remove(servlet);
+ if (handler != null)
+ {
+ updateServletMapping();
+
+ // Can be null in case of error-handling servlets...
+ String[] patterns = handler.getServletInfo().getPatterns();
+ int length = patterns == null ? 0 : patterns.length;
+
+ for (int i = 0; i < length; i++)
+ {
+ this.servletPatternMap.remove(patterns[i]);
+ }
+
+ this.errorsMapping.removeServlet(servlet);
+
+ if (destroy)
+ {
+ handler.destroy();
+ }
+ }
+ }
+
+ private boolean referencesDispatcherType(FilterHandler handler, DispatcherType dispatcherType)
+ {
+ return Arrays.asList(handler.getFilterInfo().getDispatcher()).contains(dispatcherType);
+ }
+
+ private boolean referencesServletByName(FilterHandler handler, String servletName)
+ {
+ if (servletName == null)
+ {
+ return false;
+ }
+ String[] names = handler.getFilterInfo().getServletNames();
+ if (names != null && names.length > 0)
+ {
+ return Arrays.asList(names).contains(servletName);
+ }
+ return false;
+ }
+
+ private void updateFilterMapping()
+ {
+ this.filterMapping = new HandlerMapping<FilterHandler>(this.filterMap.values());
+ }
+
+ private void updateServletMapping()
+ {
+ this.servletMapping = new HandlerMapping<ServletHandler>(this.servletMap.values());
+ }
+
+ public String getPrefixPath()
+ {
+ return this.prefixPath;
+ }
+
+ public long getContextServiceid()
+ {
+ return this.serviceId;
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
index 853b55e..7cbbc62 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
@@ -79,7 +79,7 @@
{
return 0;
}
- // service id might be negative, we have to change the behaviour in that case
+ // service id might be negative, we have to change the behavior in that case
if ( this.serviceId < 0 )
{
if ( other.serviceId > 0 )
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java
index b27646b..cae272b 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java
@@ -41,12 +41,14 @@
this.sharedContextAttributes = sharedContextAttributes;
}
+ @Override
public Object getService(Bundle bundle, ServiceRegistration reg)
{
- return new HttpServiceImpl(bundle, this.context, this.handlerRegistry, this.attributeListener,
+ return new HttpServiceImpl(bundle, this.context, this.handlerRegistry.getRegistry(null), this.attributeListener,
this.sharedContextAttributes);
}
+ @Override
public void ungetService(Bundle bundle, ServiceRegistration reg, Object service)
{
((HttpServiceImpl)service).unregisterAll();
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
index 1a73291..9062128 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
@@ -32,7 +32,7 @@
import org.apache.felix.http.base.internal.context.ExtServletContext;
import org.apache.felix.http.base.internal.context.ServletContextManager;
import org.apache.felix.http.base.internal.handler.FilterHandler;
-import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import org.apache.felix.http.base.internal.handler.PerContextHandlerRegistry;
import org.apache.felix.http.base.internal.handler.ServletHandler;
import org.apache.felix.http.base.internal.logger.SystemLogger;
import org.apache.felix.http.base.internal.runtime.FilterInfo;
@@ -44,12 +44,12 @@
public final class HttpServiceImpl implements ExtHttpService
{
private final Bundle bundle;
- private final HandlerRegistry handlerRegistry;
+ private final PerContextHandlerRegistry handlerRegistry;
private final HashSet<Servlet> localServlets;
private final HashSet<Filter> localFilters;
private final ServletContextManager contextManager;
- public HttpServiceImpl(Bundle bundle, ServletContext context, HandlerRegistry handlerRegistry, ServletContextAttributeListener servletAttributeListener, boolean sharedContextAttributes)
+ public HttpServiceImpl(Bundle bundle, ServletContext context, PerContextHandlerRegistry handlerRegistry, ServletContextAttributeListener servletAttributeListener, boolean sharedContextAttributes)
{
this.bundle = bundle;
this.handlerRegistry = handlerRegistry;
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java b/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java
index f8e4a49..cc0a4df 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java
@@ -28,7 +28,7 @@
/**
* Some convenience methods for handling URI(-parts).
- *
+ *
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class UriUtils
@@ -99,7 +99,7 @@
/**
* Concatenates two paths keeping their respective path-parts into consideration.
- *
+ *
* @param path1 the first part of the path, can be <code>null</code>;
* @param path2 the second part of the path, can be <code>null</code>.
* @return the concatenated path, can be <code>null</code> in case both given arguments were <code>null</code>.
@@ -182,7 +182,7 @@
/**
* Decodes a given URL-encoded path assuming it is UTF-8 encoded.
- *
+ *
* @param path the URL-encoded path, can be <code>null</code>.
* @return the decoded path, can be <code>null</code> only if the given path was <code>null</code>.
*/
@@ -193,7 +193,7 @@
/**
* Decodes a given URL-encoded path using a given character encoding.
- *
+ *
* @param path the URL-encoded path, can be <code>null</code>;
* @param encoding the character encoding to use, cannot be <code>null</code>.
* @return the decoded path, can be <code>null</code> only if the given path was <code>null</code>.
@@ -247,7 +247,7 @@
/**
* Removes all superfluous dot-segments using the algorithm described in RFC-3986 section 5.2.4.
- *
+ *
* @param path the path to remove all dot-segments from, can be <code>null</code>.
* @return the cleaned path, can be <code>null</code> only if the given path was <code>null</code>.
*/
@@ -420,6 +420,109 @@
return value == null || "".equals(value.trim());
}
+ public static String statusToString(final int statusCode)
+ {
+ switch (statusCode)
+ {
+ case 100:
+ return "Continue";
+ case 101:
+ return "Switching Protocols";
+ case 102:
+ return "Processing (WebDAV)";
+ case 200:
+ return "OK";
+ case 201:
+ return "Created";
+ case 202:
+ return "Accepted";
+ case 203:
+ return "Non-Authoritative Information";
+ case 204:
+ return "No Content";
+ case 205:
+ return "Reset Content";
+ case 206:
+ return "Partial Content";
+ case 207:
+ return "Multi-Status (WebDAV)";
+ case 300:
+ return "Multiple Choices";
+ case 301:
+ return "Moved Permanently";
+ case 302:
+ return "Found";
+ case 303:
+ return "See Other";
+ case 304:
+ return "Not Modified";
+ case 305:
+ return "Use Proxy";
+ case 307:
+ return "Temporary Redirect";
+ case 400:
+ return "Bad Request";
+ case 401:
+ return "Unauthorized";
+ case 402:
+ return "Payment Required";
+ case 403:
+ return "Forbidden";
+ case 404:
+ return "Not Found";
+ case 405:
+ return "Method Not Allowed";
+ case 406:
+ return "Not Acceptable";
+ case 407:
+ return "Proxy Authentication Required";
+ case 408:
+ return "Request Time-out";
+ case 409:
+ return "Conflict";
+ case 410:
+ return "Gone";
+ case 411:
+ return "Length Required";
+ case 412:
+ return "Precondition Failed";
+ case 413:
+ return "Request Entity Too Large";
+ case 414:
+ return "Request-URI Too Large";
+ case 415:
+ return "Unsupported Media Type";
+ case 416:
+ return "Requested range not satisfiable";
+ case 417:
+ return "Expectation Failed";
+ case 422:
+ return "Unprocessable Entity (WebDAV)";
+ case 423:
+ return "Locked (WebDAV)";
+ case 424:
+ return "Failed Dependency (WebDAV)";
+ case 500:
+ return "Internal Server Error";
+ case 501:
+ return "Not Implemented";
+ case 502:
+ return "Bad Gateway";
+ case 503:
+ return "Service Unavailable";
+ case 504:
+ return "Gateway Time-out";
+ case 505:
+ return "HTTP Version not supported";
+ case 507:
+ return "Insufficient Storage (WebDAV)";
+ case 510:
+ return "Not Extended";
+ default:
+ return String.valueOf(statusCode);
+ }
+ }
+
/**
* Creates a new {@link UriUtils} instance.
*/
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
index 884f0dd..8b7daca 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
@@ -103,7 +103,7 @@
servletInfo,
servlet);
try {
- this.handlerRegistry.addServlet(contextHandler.getContextInfo(), handler);
+ this.handlerRegistry.getRegistry(contextHandler.getContextInfo()).addServlet(contextHandler.getContextInfo(), handler);
} catch (ServletException e) {
// TODO create failure DTO
} catch (NamespaceException e) {
@@ -119,7 +119,7 @@
*/
public void unregisterServlet(@Nonnull final ContextHandler contextHandler, @Nonnull final ServletInfo servletInfo)
{
- final Servlet instance = this.handlerRegistry.removeServlet(contextHandler.getContextInfo(), servletInfo, true);
+ final Servlet instance = this.handlerRegistry.getRegistry(contextHandler.getContextInfo()).removeServlet(contextHandler.getContextInfo(), servletInfo, true);
if ( instance != null )
{
this.bundleContext.getServiceObjects(servletInfo.getServiceReference()).ungetService(instance);
@@ -144,7 +144,7 @@
filter,
filterInfo);
try {
- this.handlerRegistry.addFilter(handler);
+ this.handlerRegistry.getRegistry(contextHandler.getContextInfo()).addFilter(handler);
} catch (final ServletException e) {
// TODO create failure DTO
}
@@ -158,7 +158,7 @@
*/
public void unregisterFilter(@Nonnull final ContextHandler contextHandler, @Nonnull final FilterInfo filterInfo)
{
- final Filter instance = this.handlerRegistry.removeFilter(filterInfo, true);
+ final Filter instance = this.handlerRegistry.getRegistry(contextHandler.getContextInfo()).removeFilter(filterInfo, true);
if ( instance != null )
{
this.bundleContext.getServiceObjects(filterInfo.getServiceReference()).ungetService(instance);
@@ -182,7 +182,7 @@
servletInfo,
servlet);
try {
- this.handlerRegistry.addServlet(contextHandler.getContextInfo(), handler);
+ this.handlerRegistry.getRegistry(contextHandler.getContextInfo()).addServlet(contextHandler.getContextInfo(), handler);
} catch (ServletException e) {
// TODO create failure DTO
} catch (NamespaceException e) {