Return-Path: X-Original-To: apmail-felix-commits-archive@www.apache.org Delivered-To: apmail-felix-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2D90017C5F for ; Fri, 30 Jan 2015 17:22:32 +0000 (UTC) Received: (qmail 72890 invoked by uid 500); 30 Jan 2015 17:22:32 -0000 Delivered-To: apmail-felix-commits-archive@felix.apache.org Received: (qmail 72834 invoked by uid 500); 30 Jan 2015 17:22:32 -0000 Mailing-List: contact commits-help@felix.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@felix.apache.org Delivered-To: mailing list commits@felix.apache.org Received: (qmail 72823 invoked by uid 99); 30 Jan 2015 17:22:32 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 30 Jan 2015 17:22:32 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id 7EE27AC0024; Fri, 30 Jan 2015 17:22:32 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1656064 [1/2] - in /felix/trunk/http/base/src: main/java/org/apache/felix/http/base/internal/dispatch/ main/java/org/apache/felix/http/base/internal/handler/ main/java/org/apache/felix/http/base/internal/runtime/ main/java/org/apache/felix... Date: Fri, 30 Jan 2015 17:22:32 -0000 To: commits@felix.apache.org From: cziegeler@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150130172232.7EE27AC0024@hades.apache.org> Author: cziegeler Date: Fri Jan 30 17:22:31 2015 New Revision: 1656064 URL: http://svn.apache.org/r1656064 Log: FELIX-4545 : Implement Servlet Context Helper. Merge new path handling from branch Added: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java - copied, changed from r1655297, felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java Removed: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/FilterPipeline.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletPipeline.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequest.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/DefaultHttpContext.java felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerRequestTest.java Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/util/UriUtils.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/AbstractHandlerTest.java felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java felix/trunk/http/base/src/test/java/org/apache/felix/http/base/internal/util/UriUtilsTest.java Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java Fri Jan 30 17:22:31 2015 @@ -16,19 +16,374 @@ */ package org.apache.felix.http.base.internal.dispatch; +import static javax.servlet.RequestDispatcher.FORWARD_CONTEXT_PATH; +import static javax.servlet.RequestDispatcher.FORWARD_PATH_INFO; +import static javax.servlet.RequestDispatcher.FORWARD_QUERY_STRING; +import static javax.servlet.RequestDispatcher.FORWARD_REQUEST_URI; +import static javax.servlet.RequestDispatcher.FORWARD_SERVLET_PATH; +import static javax.servlet.RequestDispatcher.INCLUDE_CONTEXT_PATH; +import static javax.servlet.RequestDispatcher.INCLUDE_PATH_INFO; +import static javax.servlet.RequestDispatcher.INCLUDE_QUERY_STRING; +import static javax.servlet.RequestDispatcher.INCLUDE_REQUEST_URI; +import static javax.servlet.RequestDispatcher.INCLUDE_SERVLET_PATH; +import static org.apache.felix.http.base.internal.util.UriUtils.concat; +import static org.apache.felix.http.base.internal.util.UriUtils.decodePath; +import static org.apache.felix.http.base.internal.util.UriUtils.removeDotSegments; + import java.io.IOException; +import javax.servlet.DispatcherType; import javax.servlet.FilterChain; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.apache.felix.http.base.internal.context.ExtServletContext; +import org.apache.felix.http.base.internal.handler.ErrorsMapping; +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.HttpSessionWrapper; +import org.apache.felix.http.base.internal.handler.ServletHandler; +import org.apache.felix.http.base.internal.util.UriUtils; +import org.osgi.service.http.HttpContext; +import org.osgi.service.useradmin.Authorization; -public final class Dispatcher +public final class Dispatcher implements RequestDispatcherProvider { + /** + * Wrapper implementation for {@link RequestDispatcher}. + */ + final class RequestDispatcherImpl implements RequestDispatcher + { + private final RequestInfo requestInfo; + private final ServletHandler handler; + + public RequestDispatcherImpl(ServletHandler handler, RequestInfo requestInfo) + { + this.handler = handler; + this.requestInfo = requestInfo; + } + + @Override + public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException + { + if (response.isCommitted()) + { + throw new ServletException("Response has been committed"); + } + else + { + // See section 9.4 of Servlet 3.0 spec + response.resetBuffer(); + } + + try + { + ServletRequestWrapper req = new ServletRequestWrapper((HttpServletRequest) request, this.handler.getContext(), this.requestInfo, DispatcherType.FORWARD); + Dispatcher.this.forward(this.handler, req, (HttpServletResponse) response); + } + finally + { + // After a forward has taken place, the results should be committed, + // see section 9.4 of Servlet 3.0 spec... + if (!request.isAsyncStarted()) + { + response.flushBuffer(); + response.getWriter().close(); + } + } + } + + @Override + public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException + { + ServletRequestWrapper req = new ServletRequestWrapper((HttpServletRequest) request, this.handler.getContext(), this.requestInfo, DispatcherType.INCLUDE); + Dispatcher.this.include(this.handler, req, (HttpServletResponse) response); + } + } + + final class ServletRequestWrapper extends HttpServletRequestWrapper + { + private final DispatcherType type; + private final RequestInfo requestInfo; + private final ExtServletContext servletContext; + + public ServletRequestWrapper(HttpServletRequest req, ExtServletContext servletContext, RequestInfo requestInfo) + { + this(req, servletContext, requestInfo, null /* type */); + } + + public ServletRequestWrapper(HttpServletRequest req, ExtServletContext servletContext, RequestInfo requestInfo, DispatcherType type) + { + super(req); + + this.servletContext = servletContext; + this.requestInfo = requestInfo; + this.type = type; + } + + @Override + public Object getAttribute(String name) + { + HttpServletRequest request = (HttpServletRequest) getRequest(); + if (isInclusionDispatcher()) + { + // The javax.servlet.include.* attributes refer to the information of the *included* request, + // meaning that the request information comes from the *original* request... + if (INCLUDE_REQUEST_URI.equals(name)) + { + return concat(request.getContextPath(), this.requestInfo.requestURI); + } + else if (INCLUDE_CONTEXT_PATH.equals(name)) + { + return request.getContextPath(); + } + else if (INCLUDE_SERVLET_PATH.equals(name)) + { + return this.requestInfo.servletPath; + } + else if (INCLUDE_PATH_INFO.equals(name)) + { + return this.requestInfo.pathInfo; + } + else if (INCLUDE_QUERY_STRING.equals(name)) + { + return this.requestInfo.queryString; + } + } + else if (isForwardingDispatcher()) + { + // The javax.servlet.forward.* attributes refer to the information of the *original* request, + // meaning that the request information comes from the *forwarded* request... + if (FORWARD_REQUEST_URI.equals(name)) + { + return super.getRequestURI(); + } + else if (FORWARD_CONTEXT_PATH.equals(name)) + { + return request.getContextPath(); + } + else if (FORWARD_SERVLET_PATH.equals(name)) + { + return super.getServletPath(); + } + else if (FORWARD_PATH_INFO.equals(name)) + { + return super.getPathInfo(); + } + else if (FORWARD_QUERY_STRING.equals(name)) + { + return super.getQueryString(); + } + } + return super.getAttribute(name); + } + + @Override + public String getAuthType() + { + String authType = (String) getAttribute(HttpContext.AUTHENTICATION_TYPE); + if (authType == null) + { + authType = super.getAuthType(); + } + return authType; + } + + @Override + public String getContextPath() + { + /* + * FELIX-2030 Calculate the context path for the Http Service + * registered servlets from the container context and servlet paths + */ + // if (contextPath == null) + // { + // final String context = super.getContextPath(); + // final String servlet = super.getServletPath(); + // if (context == null || context.length() == 0) + // { + // contextPath = servlet; + // } + // else if (servlet == null || servlet.length() == 0) + // { + // contextPath = context; + // } + // else + // { + // contextPath = context + servlet; + // } + // } + + return super.getContextPath(); + } + + @Override + public DispatcherType getDispatcherType() + { + return (this.type == null) ? super.getDispatcherType() : this.type; + } + + @Override + public String getPathInfo() + { + String pathInfo = super.getPathInfo(); + if (isForwardingDispatcher() || !isWrapperFor(ServletRequestWrapper.class)) + { + pathInfo = this.requestInfo.pathInfo; + } + return pathInfo; + } + + @Override + @SuppressWarnings("deprecation") + public String getPathTranslated() + { + final String info = getPathInfo(); + return (null == info) ? null : getRealPath(info); + } + + @Override + public String getRemoteUser() + { + String remoteUser = (String) getAttribute(HttpContext.REMOTE_USER); + if (remoteUser != null) + { + return remoteUser; + } + + return super.getRemoteUser(); + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) + { + // See section 9.1 of Servlet 3.0 specification... + if (path == null) + { + return null; + } + // Handle relative paths, see Servlet 3.0 spec, section 9.1 last paragraph. + boolean relPath = !path.startsWith("/") && !"".equals(path); + if (relPath) + { + path = concat(getServletPath(), path); + } + return Dispatcher.this.getRequestDispatcher(path); + } + + @Override + public String getRequestURI() + { + String requestURI = super.getRequestURI(); + if (isForwardingDispatcher() || !isWrapperFor(ServletRequestWrapper.class)) + { + requestURI = concat(getContextPath(), this.requestInfo.requestURI); + } + return requestURI; + } + + @Override + public ServletContext getServletContext() + { + return new ServletContextWrapper(this.servletContext, Dispatcher.this); + } + + @Override + public String getServletPath() + { + String servletPath = super.getServletPath(); + if (isForwardingDispatcher() || !isWrapperFor(ServletRequestWrapper.class)) + { + servletPath = this.requestInfo.servletPath; + } + if ("/".equals(servletPath)) + { + return ""; // XXX still necessary? + } + return servletPath; + } + + @Override + public HttpSession getSession(boolean create) + { + // FELIX-2797: wrap the original HttpSession to provide access to the correct ServletContext... + HttpSession session = super.getSession(create); + if (session == null) + { + return null; + } + return new HttpSessionWrapper(session, this.servletContext); + } + + @Override + public boolean isUserInRole(String role) + { + Authorization authorization = (Authorization) getAttribute(HttpContext.AUTHORIZATION); + if (authorization != null) + { + return authorization.hasRole(role); + } + + return super.isUserInRole(role); + } + + @Override + public String toString() + { + return getClass().getSimpleName() + "->" + super.getRequest(); + } + + private boolean isForwardingDispatcher() + { + return (DispatcherType.FORWARD == this.type) && (this.requestInfo != null); + } + + private boolean isInclusionDispatcher() + { + return (DispatcherType.INCLUDE == this.type) && (this.requestInfo != null); + } + } + + private static class RequestInfo + { + final String servletPath; + final String pathInfo; + final String queryString; + final String requestURI; + + public RequestInfo(String servletPath, String pathInfo, String queryString) + { + this.servletPath = servletPath; + this.pathInfo = pathInfo; + this.queryString = queryString; + this.requestURI = UriUtils.compactPath(concat(servletPath, pathInfo)); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder("RequestInfo[servletPath ="); + sb.append(this.servletPath).append(", pathInfo = ").append(this.pathInfo); + sb.append(", queryString = ").append(this.queryString).append("]"); + return sb.toString(); + } + } + + /** + * Request attribute that provides access to the request dispatcher provider. + */ public static final String REQUEST_DISPATCHER_PROVIDER = "org.apache.felix.http.requestDispatcherProvider"; + /** + * Catch-all filter chain that simple finishes all requests with a "404 Not Found" error. + */ private static final FilterChain DEFAULT_CHAIN = new NotFoundFilterChain(); private final HandlerRegistry handlerRegistry; @@ -38,19 +393,145 @@ public final class Dispatcher this.handlerRegistry = handlerRegistry; } + /** + * Responsible for dispatching a given request to the actual applicable servlet and/or filters in the local registry. + * + * @param req the {@link ServletRequest} to dispatch; + * @param res the {@link ServletResponse} to dispatch. + * @throws ServletException in case of exceptions during the actual dispatching; + * @throws IOException in case of I/O problems. + */ public void dispatch(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { - ServletPipeline servletPipeline = new ServletPipeline(this.handlerRegistry.getServlets()); + 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); + // Provides access to the correct request dispatcher... - req.setAttribute(REQUEST_DISPATCHER_PROVIDER, servletPipeline); + req.setAttribute(REQUEST_DISPATCHER_PROVIDER, this); + + String servletPath = (servletHandler != null) ? servletHandler.determineServletPath(requestURI) : ""; + String pathInfo = UriUtils.compactPath(UriUtils.relativePath(servletPath, requestURI)); + String queryString = null; // XXX + + ExtServletContext servletContext = (servletHandler != null) ? servletHandler.getContext() : null; + RequestInfo requestInfo = new RequestInfo(servletPath, pathInfo, queryString); try { - new FilterPipeline(this.handlerRegistry.getFilters(), servletPipeline).dispatch(req, res, DEFAULT_CHAIN); + invokeChain(filterHandlers, servletHandler, new ServletRequestWrapper(req, servletContext, requestInfo), res); } finally { req.removeAttribute(REQUEST_DISPATCHER_PROVIDER); } } + + 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) + { + ServletHandler handler = this.handlerRegistry.getServletHandlerByName(name); + return handler != null ? new RequestDispatcherImpl(handler, null) : null; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) + { + // See section 9.1 of Servlet 3.x specification... + if (path == null || (!path.startsWith("/") && !"".equals(path))) + { + return null; + } + + String query = null; + int q = 0; + if ((q = path.indexOf('?')) > 0) + { + query = path.substring(q + 1); + path = path.substring(0, q); + } + // TODO remove path parameters... + String requestURI = decodePath(removeDotSegments(path)); + + ServletHandler handler = this.handlerRegistry.getServletHander(requestURI); + if (handler == null) + { + return null; + } + + String servletPath = handler.determineServletPath(requestURI); + String pathInfo = UriUtils.relativePath(servletPath, path); + + RequestInfo requestInfo = new RequestInfo(servletPath, pathInfo, query); + return new RequestDispatcherImpl(handler, requestInfo); + } + + /** + * @param servletHandler the servlet that should handle the forward request; + * @param request the {@link HttpServletRequest}; + * @param response the {@link HttpServletResponse}; + */ + void forward(ServletHandler servletHandler, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + String requestURI = getRequestURI(request); + FilterHandler[] filterHandlers = this.handlerRegistry.getFilterHandlers(servletHandler, DispatcherType.FORWARD, requestURI); + + invokeChain(filterHandlers, servletHandler, request, response); + } + + /** + * @param servletHandler the servlet that should handle the include request; + * @param request the {@link HttpServletRequest}; + * @param response the {@link HttpServletResponse}; + */ + void include(ServletHandler servletHandler, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + String requestURI = getRequestURI(request); + FilterHandler[] filterHandlers = this.handlerRegistry.getFilterHandlers(servletHandler, DispatcherType.INCLUDE, requestURI); + + invokeChain(filterHandlers, servletHandler, request, response); + } + + private String getRequestURI(HttpServletRequest req) + { + return UriUtils.relativePath(req.getContextPath(), req.getRequestURI()); + } + + private void invokeChain(FilterHandler[] filterHandlers, ServletHandler servletHandler, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + FilterChain filterChain = new InvocationFilterChain(servletHandler, filterHandlers, DEFAULT_CHAIN); + filterChain.doFilter(request, response); + } } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java Fri Jan 30 17:22:31 2015 @@ -16,20 +16,28 @@ */ package org.apache.felix.http.base.internal.dispatch; +import java.io.IOException; + import javax.servlet.FilterChain; +import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +/** + * Convenience adapter for {@link FilterChain}s that makes the obtrusive casting unnecessary. + */ public abstract class HttpFilterChain implements FilterChain { + @Override public final void doFilter(ServletRequest req, ServletResponse res) throws IOException, ServletException { doFilter((HttpServletRequest) req, (HttpServletResponse) res); } + /** + * @see FilterChain#doFilter(ServletRequest, ServletResponse) + */ protected abstract void doFilter(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException; } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java Fri Jan 30 17:22:31 2015 @@ -16,41 +16,59 @@ */ package org.apache.felix.http.base.internal.dispatch; +import static javax.servlet.http.HttpServletResponse.SC_OK; + +import java.io.IOException; + import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; + import org.apache.felix.http.base.internal.handler.FilterHandler; +import org.apache.felix.http.base.internal.handler.ServletHandler; -public final class InvocationFilterChain extends HttpFilterChain +class InvocationFilterChain extends HttpFilterChain { - private final FilterHandler[] handlers; - private final ServletPipeline servletPipeline; - private final FilterChain proceedingChain; + private final ServletHandler servletHandler; + private final FilterHandler[] filterHandlers; + private final FilterChain defaultChain; + private int index = -1; - public InvocationFilterChain(FilterHandler[] handlers, ServletPipeline servletPipeline, FilterChain proceedingChain) + public InvocationFilterChain(ServletHandler servletHandler, FilterHandler[] filterHandlers, FilterChain defaultChain) { - this.handlers = handlers; - this.servletPipeline = servletPipeline; - this.proceedingChain = proceedingChain; + this.filterHandlers = filterHandlers; + this.servletHandler = servletHandler; + this.defaultChain = defaultChain; } + @Override protected void doFilter(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { this.index++; - if (this.index < this.handlers.length) - { - this.handlers[this.index].handle(req, res, this); - } - else + if (this.index < this.filterHandlers.length) { - if (!this.servletPipeline.handle(req, res)) + if (this.filterHandlers[this.index].handle(req, res, this)) { - this.proceedingChain.doFilter(req, res); + // We're done... + return; } } + + // Last entry in the chain... + if (this.servletHandler != null && this.servletHandler.handle(req, res)) + { + // We're done... + return; + } + + // FELIX-3988: If the response is not yet committed and still has the default + // status, we're going to override this and send an error instead. + if (!res.isCommitted() && res.getStatus() == SC_OK) + { + this.defaultChain.doFilter(req, res); + } } } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java Fri Jan 30 17:22:31 2015 @@ -16,13 +16,15 @@ */ package org.apache.felix.http.base.internal.dispatch; +import java.io.IOException; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; public final class NotFoundFilterChain extends HttpFilterChain { + @Override protected void doFilter(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { res.sendError(HttpServletResponse.SC_NOT_FOUND); Copied: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java (from r1655297, felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java) URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java?p2=felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java&p1=felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java&r1=1655297&r2=1656064&rev=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletContextWrapper.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java Fri Jan 30 17:22:31 2015 @@ -17,13 +17,12 @@ * under the License. */ -package org.apache.felix.http.base.internal.handler; +package org.apache.felix.http.base.internal.dispatch; import javax.servlet.RequestDispatcher; import org.apache.felix.http.base.internal.context.ExtServletContext; import org.apache.felix.http.base.internal.context.ServletContextImpl; -import org.apache.felix.http.base.internal.dispatch.RequestDispatcherProvider; /** * @author Felix Project Team Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java Fri Jan 30 17:22:31 2015 @@ -16,11 +16,10 @@ */ package org.apache.felix.http.base.internal.handler; -import java.util.Dictionary; -import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; import javax.servlet.Filter; import javax.servlet.Servlet; @@ -28,7 +27,7 @@ import javax.servlet.ServletException; import org.apache.felix.http.base.internal.context.ExtServletContext; -public abstract class AbstractHandler +public abstract class AbstractHandler implements Comparable { private final static AtomicInteger ID = new AtomicInteger(); @@ -76,32 +75,7 @@ public abstract class AbstractHandler public abstract void init() throws ServletException; - /** - * TODO - We can remove this, once we switched to {@link #setInitParams(Map)} - * @param map - */ - public final void setInitParams(Dictionary map) - { - this.initParams.clear(); - if (map == null) - { - return; - } - - Enumeration e = map.keys(); - while (e.hasMoreElements()) - { - Object key = e.nextElement(); - Object value = map.get(key); - - if ((key instanceof String) && (value instanceof String)) - { - this.initParams.put((String) key, (String) value); - } - } - } - - protected final ExtServletContext getContext() + public ExtServletContext getContext() { return this.context; } @@ -118,4 +92,6 @@ public abstract class AbstractHandler * @return the {@link Servlet} or {@link Filter} this handler handles. */ protected abstract Object getSubject(); + + public abstract Pattern[] getPatterns(); } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java Fri Jan 30 17:22:31 2015 @@ -20,6 +20,8 @@ import static javax.servlet.http.HttpSer import static javax.servlet.http.HttpServletResponse.SC_OK; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.regex.Pattern; import javax.servlet.Filter; @@ -30,32 +32,33 @@ import javax.servlet.http.HttpServletRes import org.apache.felix.http.base.internal.context.ExtServletContext; import org.apache.felix.http.base.internal.runtime.FilterInfo; +import org.apache.felix.http.base.internal.util.PatternUtil; -public final class FilterHandler extends AbstractHandler implements Comparable +public final class FilterHandler extends AbstractHandler { private final Filter filter; private final FilterInfo filterInfo; + private final Pattern[] patterns; public FilterHandler(ExtServletContext context, Filter filter, FilterInfo filterInfo) { - super(context, filterInfo.initParams, filterInfo.name); + super(context, filterInfo.getInitParams(), filterInfo.getName()); this.filter = filter; this.filterInfo = filterInfo; + // Compose a single array of all patterns & regexs the filter must represent... + String[] patterns = getFilterPatterns(filterInfo); + + this.patterns = new Pattern[patterns.length]; + for (int i = 0; i < patterns.length; i++) + { + this.patterns[i] = Pattern.compile(patterns[i]); + } } @Override public int compareTo(FilterHandler other) { - if (other.filterInfo.ranking == this.filterInfo.ranking) - { - if (other.filterInfo.serviceId == this.filterInfo.serviceId) - { - return 0; - } - return other.filterInfo.serviceId > this.filterInfo.serviceId ? -1 : 1; - } - - return (other.filterInfo.ranking > this.filterInfo.ranking) ? 1 : -1; + return this.filterInfo.compareTo(other.filterInfo); } @Override @@ -69,59 +72,33 @@ public final class FilterHandler extends return this.filter; } - public String getPattern() + public FilterInfo getFilterInfo() { - final StringBuilder sb = new StringBuilder(); - boolean first = true; - if ( this.filterInfo.regexs != null ) - { - for(final String p : this.filterInfo.regexs) - { - if ( first ) - { - first = false; - } - else - { - sb.append(", "); - } - sb.append(p); - } - } - if ( this.filterInfo.patterns != null ) - { - for(final String p : this.filterInfo.patterns) - { - if ( first ) - { - first = false; - } - else - { - sb.append(", "); - } - sb.append(p); - } - } - return sb.toString(); + return this.filterInfo; } public int getRanking() { - return filterInfo.ranking; + return filterInfo.getRanking(); } - public void handle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException + public boolean handle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException { - final boolean matches = matches(req.getPathInfo()); - if (matches) + if (getContext().handleSecurity(req, res)) { - doHandle(req, res, chain); + this.filter.doFilter(req, res, chain); + + return true; } - else + + // FELIX-3988: If the response is not yet committed and still has the default + // status, we're going to override this and send an error instead. + if (!res.isCommitted() && (res.getStatus() == SC_OK || res.getStatus() == 0)) { - chain.doFilter(req, res); + res.sendError(SC_FORBIDDEN); } + + return false; } @Override @@ -130,60 +107,34 @@ public final class FilterHandler extends this.filter.init(new FilterConfigImpl(getName(), getContext(), getInitParams())); } - public boolean matches(String uri) + @Override + protected Object getSubject() { - // assume root if uri is null - if (uri == null) - { - uri = "/"; - } - - if ( this.filterInfo.patterns != null ) - { - for(final String p : this.filterInfo.patterns) - { - if ( Pattern.compile(p.replace(".", "\\.").replace("*", ".*")).matcher(uri).matches() ) - { - return true; - } - } - } - if ( this.filterInfo.regexs != null ) - { - for(final String p : this.filterInfo.regexs) - { - if ( Pattern.compile(p).matcher(uri).matches() ) - { - return true; - } - } - } - - // TODO implement servlet name matching - - return false; + return this.filter; } - final void doHandle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException + private static String[] getFilterPatterns(FilterInfo filterInfo) { - if (getContext().handleSecurity(req, res)) + List result = new ArrayList(); + if (filterInfo.getPatterns() != null) { - this.filter.doFilter(req, res, chain); + for (int i = 0; i < filterInfo.getPatterns().length; i++) + { + result.add(PatternUtil.convertToRegEx(filterInfo.getPatterns()[i])); + } } - else + if (filterInfo.getRegexs() != null) { - // FELIX-3988: If the response is not yet committed and still has the default - // status, we're going to override this and send an error instead. - if (!res.isCommitted() && (res.getStatus() == SC_OK || res.getStatus() == 0)) + for (int i = 0; i < filterInfo.getRegexs().length; i++) { - res.sendError(SC_FORBIDDEN); + result.add(filterInfo.getRegexs()[i]); } } + return result.toArray(new String[result.size()]); } @Override - protected Object getSubject() - { - return this.filter; + public Pattern[] getPatterns() { + return this.patterns; } } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java Fri Jan 30 17:22:31 2015 @@ -16,129 +16,234 @@ */ package org.apache.felix.http.base.internal.handler; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.HashMap; import java.util.Iterator; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +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.ContextInfo; +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 ConcurrentMap filterMap = new ConcurrentHashMap(); - private final ConcurrentMap aliasMap = new ConcurrentHashMap(); - private volatile ServletHandler[] servlets; - private volatile FilterHandler[] filters; + private final Map servletMap = new HashMap(); + private final Map filterMap = new HashMap(); + private final Map servletPatternMap= new HashMap(); + private volatile HandlerMapping servletMapping = new HandlerMapping(); + private volatile HandlerMapping filterMapping = new HandlerMapping(); + private volatile ErrorsMapping errorsMapping = new ErrorsMapping(); - public HandlerRegistry() + public synchronized void addErrorServlet(String errorPage, ServletHandler handler) throws ServletException { - servlets = new ServletHandler[0]; - filters = new FilterHandler[0]; + this.errorsMapping.addErrorServlet(errorPage, handler); } - public ServletHandler[] getServlets() + public synchronized void addFilter(FilterHandler handler) throws ServletException { - return servlets; + if (this.filterMap.containsKey(handler.getFilter())) + { + throw new ServletException("Filter instance already registered"); + } + + handler.init(); + this.filterMap.put(handler.getFilter(), handler); + + updateFilterMapping(); + } + + /** + * Add a new servlet. + */ + public synchronized void addServlet(ContextInfo 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()); + } + + handler.init(); + this.servletMap.put(handler.getServlet(), handler); + + updateServletMapping(); } - public FilterHandler[] getFilters() + public ErrorsMapping getErrorsMapping() { - return filters; + return this.errorsMapping; } - public void addServlet(final ServletHandler handler) - throws ServletException, NamespaceException + public FilterHandler[] getFilterHandlers(ServletHandler servletHandler, DispatcherType dispatcherType, String requestURI) { - handler.init(); + // See Servlet 3.0 specification, section 6.2.4... + List result = new ArrayList(); + result.addAll(this.filterMapping.getAllMatches(requestURI)); - if (aliasMap.putIfAbsent(handler.getAlias(), handler) != null) + // TODO this is not the most efficient/fastest way of doing this... + Iterator iter = result.iterator(); + while (iter.hasNext()) { - handler.destroy(); - throw new NamespaceException("Servlet with alias '" + handler.getAlias() + "' already registered"); + if (!referencesDispatcherType(iter.next(), dispatcherType)) + { + iter.remove(); + } } - updateServletArray(); + 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); + } + } + + return result.toArray(new FilterHandler[result.size()]); } - public void addFilter(FilterHandler handler) throws ServletException + public synchronized Servlet getServletByAlias(String alias) { - handler.init(); + return this.servletPatternMap.get(alias); + } + + public ServletHandler getServletHandlerByName(String name) + { + return this.servletMapping.getByName(name); + } + + public ServletHandler getServletHander(String requestURI) + { + return this.servletMapping.getBestMatch(requestURI); + } - // there is a window of opportunity that the servlet/alias was registered between the - // previous check and this one, so we have to atomically add if absent here. - if (filterMap.putIfAbsent(handler.getFilter(), handler) != null) + public synchronized void removeAll() + { + for (Iterator it = servletMap.values().iterator(); it.hasNext(); ) { - // Do not destroy the filter as the same instance was already registered - throw new ServletException("Filter instance already registered"); + ServletHandler handler = it.next(); + it.remove(); + handler.destroy(); + } + + for (Iterator it = filterMap.values().iterator(); it.hasNext(); ) + { + FilterHandler handler = it.next(); + it.remove(); + handler.destroy(); } - updateFilterArray(); + this.servletMap.clear(); + this.filterMap.clear(); + this.servletPatternMap.clear(); + this.errorsMapping.clear(); + + updateServletMapping(); + updateFilterMapping(); } - public Set removeServlet(final ServletInfo servletInfo, final boolean destroy) + public synchronized void removeFilter(Filter filter, final boolean destroy) { - final Set servletInstances = new HashSet(); - boolean update = false; - for (Iterator it = aliasMap.values().iterator(); it.hasNext(); ) + FilterHandler handler = this.filterMap.remove(filter); + if (handler != null) { - final ServletHandler handler = it.next(); - if (handler.getServletInfo().compareTo(servletInfo) == 0 ) { - it.remove(); - servletInstances.add(handler.getServlet()); + 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(); } - update = true; + return handler.getFilter(); } } - if ( update ) - { - updateServletArray(); - } - - return servletInstances; + return null; } - /** - * Support for old Http Service registrations - * @param servlet - * @param destroy - */ - public void removeServlet(final Servlet servlet, final boolean destroy) + public synchronized Servlet removeServlet(final ContextInfo contextInfo, ServletInfo servletInfo, final boolean destroy) { - boolean update = false; - for (Iterator it = aliasMap.values().iterator(); it.hasNext(); ) + for(final ServletHandler handler : this.servletMap.values()) { - ServletHandler handler = it.next(); - if (handler.getServlet() == servlet ) { - it.remove(); + 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(); } - update = true; + return handler.getServlet(); } } - if ( update ) - { - updateServletArray(); - } + return null; } - public void removeFilter(Filter filter, final boolean destroy) + public synchronized void removeServlet(Servlet servlet, final boolean destroy) { - FilterHandler handler = filterMap.remove(filter); + ServletHandler handler = this.servletMap.remove(servlet); if (handler != null) { - updateFilterArray(); + 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(); @@ -146,52 +251,32 @@ public final class HandlerRegistry } } - public Servlet getServletByAlias(String alias) + private boolean referencesDispatcherType(FilterHandler handler, DispatcherType dispatcherType) { - final ServletHandler handler = aliasMap.get(alias); - if ( handler != null ) { - return handler.getServlet(); - } - return null; + return Arrays.asList(handler.getFilterInfo().getDispatcher()).contains(dispatcherType); } - public void addErrorServlet(String errorPage, ServletHandler handler) throws ServletException + private boolean referencesServletByName(FilterHandler handler, String servletName) { - // TODO - } - - public void removeAll() - { - for (Iterator it = aliasMap.values().iterator(); it.hasNext(); ) + if (servletName == null) { - ServletHandler handler = it.next(); - it.remove(); - - handler.destroy(); + return false; } - - for (Iterator it = filterMap.values().iterator(); it.hasNext(); ) + String[] names = handler.getFilterInfo().getServletNames(); + if (names != null && names.length > 0) { - FilterHandler handler = it.next(); - it.remove(); - handler.destroy(); + return Arrays.asList(names).contains(servletName); } - - updateServletArray(); - updateFilterArray(); + return false; } - private void updateServletArray() + private void updateFilterMapping() { - final ServletHandler[] tmp = aliasMap.values().toArray(new ServletHandler[aliasMap.size()]); - Arrays.sort(tmp); - servlets = tmp; + this.filterMapping = new HandlerMapping(this.filterMap.values()); } - private void updateFilterArray() + private void updateServletMapping() { - final FilterHandler[] tmp = filterMap.values().toArray(new FilterHandler[filterMap.size()]); - Arrays.sort(tmp); - filters = tmp; + this.servletMapping = new HandlerMapping(this.servletMap.values()); } } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java Fri Jan 30 17:22:31 2015 @@ -53,7 +53,7 @@ public class HttpServicePlugin extends H public void register() { - final Dictionary props = new Hashtable(); + Dictionary props = new Hashtable(); props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation"); props.put(Constants.SERVICE_DESCRIPTION, "HTTP Service Web Console Plugin"); props.put("felix.webconsole.label", "httpservice"); @@ -88,14 +88,14 @@ public class HttpServicePlugin extends H pw.println("${Bundle}"); pw.println(""); - FilterHandler[] filters = registry.getFilters(); + FilterHandler[] filters = new FilterHandler[0]; // XXX was: registry.getFilters(); Arrays.sort(filters); String rowClass = "odd"; for (FilterHandler filter : filters) { pw.println(""); - pw.println("" + filter.getPattern() + ""); - pw.println("" + filter.getFilter().getClass().getName() + "(" + filter.getRanking() + ")" + ""); +// pw.println("" + Arrays.toString(filter.getPatternStrings()) + ""); // XXX +// pw.println("" + filter.getFilter().getClass().getName() + "(" + filter.getRanking() + ")" + ""); printBundleDetails(pw, filter.getFilter().getClass()); @@ -122,14 +122,14 @@ public class HttpServicePlugin extends H pw.println("${Bundle}"); pw.println(""); - ServletHandler[] servlets = registry.getServlets(); + ServletHandler[] servlets = new ServletHandler[0]; // XXX was: registry.getServlets(); String rowClass = "odd"; for (ServletHandler servlet : servlets) { pw.println(""); - pw.println("" + servlet.getAlias() + ""); - pw.println("" + servlet.getServlet().getClass().getName() + ""); +// pw.println("" + Arrays.toString(servlet.getPatternStrings()) + ""); // XXX +// pw.println("" + servlet.getServlet().getClass().getName() + ""); printBundleDetails(pw, servlet.getServlet().getClass()); @@ -154,33 +154,30 @@ public class HttpServicePlugin extends H pw.println("HTTP Service Details:"); pw.println(); pw.println("Registered Servlet Services"); - ServletHandler[] servlets = registry.getServlets(); + ServletHandler[] servlets = new ServletHandler[0]; // XXX was: registry.getServlets(); for (ServletHandler servlet : servlets) { - pw.println("Alias : " + servlet.getAlias()); - +// pw.println("Patterns : " + Arrays.toString(servlet.getPatternStrings())); // XXX addSpace(pw, 1); - pw.println("Class :" + servlet.getServlet().getClass().getName()); + pw.println("Class : " + servlet.getServlet().getClass().getName()); addSpace(pw, 1); - pw.println("Bundle :" + getBundleDetails(servlet.getServlet().getClass())); - + pw.println("Bundle : " + getBundleDetails(servlet.getServlet().getClass())); } pw.println(); pw.println("Registered Filter Services"); - FilterHandler[] filters = registry.getFilters(); + FilterHandler[] filters = new FilterHandler[0]; // XXX was: registry.getFilters(); Arrays.sort(filters); for (FilterHandler filter : filters) { - pw.println("Pattern : " + filter.getPattern()); - +// pw.println("Patterns : " + Arrays.toString(filter.getPatternStrings())); // XXX addSpace(pw, 1); - pw.println("Ranking :" + filter.getRanking()); +// pw.println("Ranking : " + filter.getRanking()); // XXX addSpace(pw, 1); - pw.println("Class :" + filter.getFilter().getClass().getName()); + pw.println("Class : " + filter.getFilter().getClass().getName()); addSpace(pw, 1); - pw.println("Bundle :" + getBundleDetails(filter.getFilter().getClass())); + pw.println("Bundle : " + getBundleDetails(filter.getFilter().getClass())); } } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java Fri Jan 30 17:22:31 2015 @@ -16,264 +16,85 @@ */ package org.apache.felix.http.base.internal.handler; -import static javax.servlet.RequestDispatcher.FORWARD_CONTEXT_PATH; -import static javax.servlet.RequestDispatcher.FORWARD_PATH_INFO; -import static javax.servlet.RequestDispatcher.FORWARD_QUERY_STRING; -import static javax.servlet.RequestDispatcher.FORWARD_REQUEST_URI; -import static javax.servlet.RequestDispatcher.FORWARD_SERVLET_PATH; -import static javax.servlet.RequestDispatcher.INCLUDE_CONTEXT_PATH; -import static javax.servlet.RequestDispatcher.INCLUDE_PATH_INFO; -import static javax.servlet.RequestDispatcher.INCLUDE_QUERY_STRING; -import static javax.servlet.RequestDispatcher.INCLUDE_REQUEST_URI; -import static javax.servlet.RequestDispatcher.INCLUDE_SERVLET_PATH; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; import static javax.servlet.http.HttpServletResponse.SC_OK; -import static org.apache.felix.http.base.internal.util.UriUtils.concat; import java.io.IOException; +import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.servlet.DispatcherType; -import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import org.apache.felix.http.base.internal.context.ExtServletContext; +import org.apache.felix.http.base.internal.runtime.ContextInfo; import org.apache.felix.http.base.internal.runtime.ServletInfo; +import org.apache.felix.http.base.internal.util.PatternUtil; /** * @author Felix Project Team */ -public final class ServletHandler extends AbstractHandler implements Comparable +public final class ServletHandler extends AbstractHandler { - private class RequestDispatcherImpl implements RequestDispatcher - { - final String servletPath = "/".equals(getAlias()) ? "" : getAlias(); // XXX handle wildcard aliases! - final String requestURI; - final String pathInfo; - final String query; - final boolean named; - - public RequestDispatcherImpl() - { - this.requestURI = null; - this.pathInfo = null; - this.query = null; - this.named = true; - } + private final ServletInfo servletInfo; - public RequestDispatcherImpl(String uri, String pathInContext, String query) - { - this.requestURI = uri; - this.pathInfo = this.servletPath.equals(pathInContext) ? null : pathInContext; - this.query = query; - this.named = false; - } + private final Servlet servlet; - @Override - public void forward(ServletRequest req, ServletResponse res) throws ServletException, IOException - { - if (res.isCommitted()) - { - throw new ServletException("Response has been committed"); - } - else - { - // See section 9.4 of Servlet 3.0 spec - res.resetBuffer(); - } - - // Since we're already created this RequestDispatcher for *this* servlet handler, we do not need to - // recheck whether its patch matches, but instead can directly handle the forward-request... - doHandle(new ServletRequestWrapper((HttpServletRequest) req, this, DispatcherType.FORWARD), (HttpServletResponse) res); - - // After a forward has taken place, the results should be committed, - // see section 9.4 of Servlet 3.0 spec... - if (!req.isAsyncStarted()) - { - res.flushBuffer(); - res.getWriter().close(); - } - } + private final Pattern[] patterns; - @Override - public void include(ServletRequest req, ServletResponse res) throws ServletException, IOException - { - // Since we're already created this RequestDispatcher for *this* servlet handler, we do not need to - // recheck whether its patch matches, but instead can directly handle the include-request... - doHandle(new ServletRequestWrapper((HttpServletRequest) req, this, DispatcherType.INCLUDE), (HttpServletResponse) res); - } + private final long contextServiceId; - boolean isNamedDispatcher() - { - return this.named; - } - } - - private static class ServletRequestWrapper extends HttpServletRequestWrapper + public ServletHandler(final ContextInfo contextInfo, + final ExtServletContext context, + final ServletInfo servletInfo, + final Servlet servlet) { - private final RequestDispatcherImpl dispatcher; - private final DispatcherType type; - - public ServletRequestWrapper(HttpServletRequest req, RequestDispatcherImpl dispatcher, DispatcherType type) - { - super(req); - this.dispatcher = dispatcher; - this.type = type; - } - - @Override - public Object getAttribute(String name) - { - HttpServletRequest request = (HttpServletRequest) getRequest(); - if (isInclusionDispatcher()) - { - if (INCLUDE_REQUEST_URI.equals(name)) - { - return concat(request.getContextPath(), this.dispatcher.requestURI); - } - else if (INCLUDE_CONTEXT_PATH.equals(name)) - { - return request.getContextPath(); - } - else if (INCLUDE_SERVLET_PATH.equals(name)) - { - return this.dispatcher.servletPath; - } - else if (INCLUDE_PATH_INFO.equals(name)) - { - return this.dispatcher.pathInfo; - } - else if (INCLUDE_QUERY_STRING.equals(name)) - { - return this.dispatcher.query; - } - } - else if (isForwardingDispatcher()) - { - // NOTE: the forward.* attributes *always* yield the *original* values... - if (FORWARD_REQUEST_URI.equals(name)) - { - return super.getRequestURI(); - } - else if (FORWARD_CONTEXT_PATH.equals(name)) - { - return request.getContextPath(); - } - else if (FORWARD_SERVLET_PATH.equals(name)) - { - return super.getServletPath(); - } - else if (FORWARD_PATH_INFO.equals(name)) - { - return super.getPathInfo(); - } - else if (FORWARD_QUERY_STRING.equals(name)) - { - return super.getQueryString(); - } - } - - return super.getAttribute(name); - } - - @Override - public DispatcherType getDispatcherType() - { - return this.type; - } - - @Override - public String getPathInfo() - { - if (isForwardingDispatcher()) - { - return this.dispatcher.pathInfo; - } - return super.getPathInfo(); - } - - @Override - public String getRequestURI() - { - if (isForwardingDispatcher()) - { - return concat(getContextPath(), this.dispatcher.requestURI); - } - return super.getRequestURI(); - } + super(context, servletInfo.getInitParams(), servletInfo.getName()); + this.servlet = servlet; + this.servletInfo = servletInfo; - @Override - public String getServletPath() - { - if (isForwardingDispatcher()) - { - return this.dispatcher.servletPath; - } - return super.getServletPath(); - } + // Can be null in case of error-handling servlets... + String[] patterns = this.servletInfo.getPatterns(); + final int length = patterns == null ? 0 : patterns.length; - @Override - public String toString() + this.patterns = new Pattern[length]; + for (int i = 0; i < length; i++) { - return getClass().getSimpleName() + "->" + super.getRequest(); + String pattern = (contextInfo == null ? patterns[i] : contextInfo.getFullPath(patterns[i])); + this.patterns[i] = Pattern.compile(PatternUtil.convertToRegEx(pattern)); } - - private boolean isForwardingDispatcher() + if ( contextInfo != null ) { - return DispatcherType.FORWARD == this.type && !this.dispatcher.isNamedDispatcher(); + this.contextServiceId = contextInfo.getServiceId(); } - - private boolean isInclusionDispatcher() + else { - return DispatcherType.INCLUDE == this.type && !this.dispatcher.isNamedDispatcher(); + this.contextServiceId = -1; } } - private final ServletInfo servletInfo; - - private final Servlet servlet; - - private final Pattern pattern; - - private final String alias; - - public ServletHandler(final ExtServletContext context, - final ServletInfo servletInfo, - final Servlet servlet, - final String alias) + @Override + public int compareTo(ServletHandler other) { - super(context, servletInfo.getInitParams(), servletInfo.getName()); - this.servlet = servlet; - this.pattern = Pattern.compile(alias.replace(".", "\\.").replace("*", ".*")); - this.alias = alias; - this.servletInfo = servletInfo; + return getId() - other.getId(); } - @Override - public int compareTo(ServletHandler other) + public String determineServletPath(String uri) { - int result = other.alias.length() - this.alias.length(); - if ( result == 0 ) + if (uri == null) { - result = this.alias.compareTo(other.alias); + uri = "/"; } - return result; - } - public RequestDispatcher createNamedRequestDispatcher() - { - return new RequestDispatcherImpl(); - } + Matcher matcher = this.patterns[0].matcher(uri); + if (matcher.find(0)) + { + return matcher.groupCount() > 0 ? matcher.group(1) : matcher.group(); + } - public RequestDispatcher createRequestDispatcher(String path, String pathInContext, String query) - { - return new RequestDispatcherImpl(path, pathInContext, query); + return null; } @Override @@ -282,43 +103,15 @@ public final class ServletHandler extend this.servlet.destroy(); } - public String getAlias() - { - return this.alias; - } - public Servlet getServlet() { return this.servlet; } - public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException + @Override + public Pattern[] getPatterns() { - String path; - if (DispatcherType.INCLUDE == req.getDispatcherType()) - { - path = (String) req.getAttribute(INCLUDE_SERVLET_PATH); - } - else if (DispatcherType.FORWARD == req.getDispatcherType()) - { - path = (String) req.getAttribute(FORWARD_SERVLET_PATH); - } - else if (DispatcherType.ASYNC == req.getDispatcherType()) - { - path = (String) req.getAttribute("javax.servlet.async.path_info"); - } - else - { - path = req.getPathInfo(); - } - - final boolean matches = matches(path); - if (matches) - { - doHandle(req, res); - } - - return matches; + return this.patterns; } @Override @@ -327,22 +120,23 @@ public final class ServletHandler extend this.servlet.init(new ServletConfigImpl(getName(), getContext(), getInitParams())); } - public boolean matches(String uri) + public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { - // TODO handle wildcard aliases and extension specs... - if (uri == null) - { - return this.alias.equals("/"); - } - else if (this.alias.equals("/")) + if (getContext().handleSecurity(req, res)) { - return uri.startsWith(this.alias); + this.servlet.service(req, res); + + return true; } - else if ( uri.equals(this.alias) || uri.startsWith(this.alias + "/") ) + + // FELIX-3988: If the response is not yet committed and still has the default + // status, we're going to override this and send an error instead. + if (!res.isCommitted() && (res.getStatus() == SC_OK || res.getStatus() == 0)) { - return true; + res.sendError(SC_FORBIDDEN); } - return this.pattern.matcher(uri).matches(); + + return false; } public ServletInfo getServletInfo() @@ -350,33 +144,14 @@ public final class ServletHandler extend return this.servletInfo; } - final void doHandle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException - { - // Only wrap the original ServletRequest in case we're handling plain requests, - // not inclusions or forwards from servlets. Should solve FELIX-2774 and FELIX-3054... - if (DispatcherType.REQUEST == req.getDispatcherType()) - { - req = new ServletHandlerRequest(req, getContext(), this.alias); - } - - if (getContext().handleSecurity(req, res)) - { - this.servlet.service(req, res); - } - else - { - // FELIX-3988: If the response is not yet committed and still has the default - // status, we're going to override this and send an error instead. - if (!res.isCommitted() && res.getStatus() == SC_OK) - { - res.sendError(SC_FORBIDDEN); - } - } - } - @Override protected Object getSubject() { return this.servlet; } + + public long getContextServiceId() + { + return this.contextServiceId; + } } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java Fri Jan 30 17:22:31 2015 @@ -119,6 +119,19 @@ public abstract class AbstractInfo im { return 0; } + // service id might be negative, we have to change the behaviour 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; } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java Fri Jan 30 17:22:31 2015 @@ -31,10 +31,21 @@ public final class ContextInfo extends A private final String path; + private final String prefix; + public ContextInfo(final ServiceReference ref) { super(ref); this.name = this.getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME); this.path = this.getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH); + String prefix = null; + if ( !isEmpty(this.path) ) + { + if ( !this.path.equals("/") && this.path.length() > 1 ) + { + prefix = this.path.substring(0, this.path.length() - 1); + } + } + this.prefix = prefix; } @Override @@ -53,4 +64,18 @@ public final class ContextInfo extends A { return this.path; } + + public String getPrefix() + { + return this.prefix; + } + + public String getFullPath(final String path) + { + if ( this.prefix == null ) + { + return path; + } + return this.prefix.concat(path); + } } Modified: felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java URL: http://svn.apache.org/viewvc/felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java?rev=1656064&r1=1656063&r2=1656064&view=diff ============================================================================== --- felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java (original) +++ felix/trunk/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java Fri Jan 30 17:22:31 2015 @@ -24,10 +24,10 @@ import javax.servlet.DispatcherType; import javax.servlet.Filter; import org.osgi.dto.DTO; +import org.osgi.framework.ServiceReference; import org.osgi.service.http.HttpContext; import org.osgi.service.http.runtime.dto.FilterDTO; - -import aQute.bnd.annotation.ConsumerType; +import org.osgi.service.http.whiteboard.HttpWhiteboardConstants; /** * Provides registration information for a {@link Filter}, and is used to programmatically register {@link Filter}s. @@ -37,13 +37,18 @@ import aQute.bnd.annotation.ConsumerType * * @author Felix Project Team */ -@ConsumerType -public final class FilterInfo +public final class FilterInfo extends AbstractInfo { /** - * The name of the servlet. + * Properties starting with this prefix are passed as filter init parameters to the + * {@code init()} method of the filter. + */ + private static final String FILTER_INIT_PREFIX = "filter.init."; + + /** + * The name of the filter. */ - public String name; + private final String name; /** * The request mappings for the servlet. @@ -52,7 +57,7 @@ public final class FilterInfo * Note that these patterns should conform to the Servlet specification. *

*/ - public String[] patterns; + private final String[] patterns; /** * The servlet names for the servlet filter. @@ -60,7 +65,7 @@ public final class FilterInfo * The specified names are used to determine the servlets whose requests are mapped to the servlet filter. *

*/ - public String[] servletNames; + private final String[] servletNames; /** * The request mappings for the servlet filter. @@ -69,18 +74,12 @@ public final class FilterInfo * These regular expressions are a convenience extension allowing one to specify filters that match paths that are difficult to match with plain Servlet patterns alone. *

*/ - public String[] regexs; + private final String[] regexs; /** * Specifies whether the servlet filter supports asynchronous processing. */ - public boolean asyncSupported = false; - - /** - * Specifies the ranking order in which this filter should be called. Higher rankings are called first. - */ - public int ranking = 0; - public long serviceId; + private final boolean asyncSupported; /** * The dispatcher associations for the servlet filter. @@ -89,16 +88,108 @@ public final class FilterInfo * See {@link DispatcherType} and Servlet 3.0 specification, section 6.2.5. *

*/ - public DispatcherType[] dispatcher = { DispatcherType.REQUEST }; + private final DispatcherType[] dispatcher; /** * The filter initialization parameters as provided during registration of the filter. */ - public Map initParams; + private final Map initParams; /** - * The {@link HttpContext} for the servlet. + * The {@link HttpContext} for the filter. */ - public HttpContext context; + private final HttpContext context; + + private final Filter filter; + public FilterInfo(final ServiceReference ref) + { + super(ref); + this.name = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME); + this.asyncSupported = getBooleanProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED); + this.servletNames = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET); + this.patterns = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN); + this.regexs = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX); + this.initParams = getInitParams(ref, FILTER_INIT_PREFIX); + String[] dispatcherNames = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER); + if (dispatcherNames != null && dispatcherNames.length > 0) + { + DispatcherType[] dispatchers = new DispatcherType[dispatcherNames.length]; + for (int i = 0; i < dispatchers.length; i++) + { + dispatchers[i] = DispatcherType.valueOf(dispatcherNames[i].toUpperCase()); + } + this.dispatcher = dispatchers; + } + else + { + this.dispatcher = new DispatcherType[] {DispatcherType.REQUEST}; + } + this.context = null; + this.filter = null; + } + + /** + * Constructor for Http Service + */ + public FilterInfo(final String name, + final String regex, + final int serviceRanking, + final Map initParams, + final Filter filter, + final HttpContext context) + { + super(serviceRanking); + this.name = name; + this.patterns = null; + this.servletNames = null; + this.regexs = new String[] {regex}; + this.initParams = initParams; + this.asyncSupported = false; + this.dispatcher = new DispatcherType[] {DispatcherType.REQUEST}; + this.filter = filter; + this.context = context; + } + + @Override + public boolean isValid() + { + return super.isValid() && (!isEmpty(this.patterns) || !isEmpty(this.regexs) || !isEmpty(this.servletNames)); + } + + public String getName() { + return name; + } + + public String[] getPatterns() { + return patterns; + } + + public String[] getServletNames() { + return servletNames; + } + + public String[] getRegexs() { + return regexs; + } + + public boolean isAsyncSupported() { + return asyncSupported; + } + + public DispatcherType[] getDispatcher() { + return dispatcher; + } + + public Map getInitParams() { + return initParams; + } + + public HttpContext getContext() { + return context; + } + + public Filter getFilter() { + return filter; + } }