Author: bdekruijff at gmail.com Date: Tue Jan 25 14:25:33 2011 New Revision: 706
Log: AMDATU-245 initial version (lots of todos) Added: sandbox/bdekruijff/dispatcher/ sandbox/bdekruijff/dispatcher/pom.xml sandbox/bdekruijff/dispatcher/src/ sandbox/bdekruijff/dispatcher/src/main/ sandbox/bdekruijff/dispatcher/src/main/java/ sandbox/bdekruijff/dispatcher/src/main/java/org/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchExtenderFilter.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchFilterMatcher.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchServletMatcher.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatcherService.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/DefaultHttpContext.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/ExtServletContext.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/HandlerServletContext.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/HttpContextManager.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/FilterPipeline.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/HttpFilterChain.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/InvocationFilterChain.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/NotFoundFilterChain.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/ServletPipeline.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/extender/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/extender/TenantParameterDispatchExtender.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/filter/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/filter/DispatchInterceptFilter.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/AbstractHandler.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/AbstractHandlerRegistry.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterConfigImpl.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterHandler.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterHandlerRegistry.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletConfigImpl.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandler.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandlerRegistry.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandlerRequest.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/matcher/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/matcher/TenantAwareServletMatcher.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/matcher/TenantUnawareServletMatcher.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/osgi/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/osgi/DispatcherServiceActivator.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/service/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/service/DispatcherServiceImpl.java sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/test/ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/test/HelloWorldServlet.java Added: sandbox/bdekruijff/dispatcher/pom.xml ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/pom.xml Tue Jan 25 14:25:33 2011 @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.amdatu</groupId> + <artifactId>org.amdatu.web</artifactId> + <version>0.1.0-SNAPSHOT</version> + </parent> + <groupId>org.amdatu.web</groupId> + <artifactId>dispatcher</artifactId> + <packaging>bundle</packaging> + <name>Amdatu Web - Dispatcher</name> + <description>Provides a Dispatcher implementation</description> + <dependencies> + <dependency> + <groupId>org.amdatu.core</groupId> + <artifactId>tenant</artifactId> + <scope>provided</scope> + <type>bundle</type> + <version>0.1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.http.api</artifactId> + <version>2.0.4</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.http.base</artifactId> + <version>2.0.4</version> + <scope>compile</scope> + </dependency> + <!-- + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.http.whiteboard</artifactId> + <version>2.0.4</version> + <scope>compile</scope> + </dependency> + --> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Private-Package> org.amdatu.web.dispatcher.*</Private-Package> + <Bundle-Activator>org.amdatu.web.dispatcher.osgi.DispatcherServiceActivator</Bundle-Activator> + <Bundle-SymbolicName>org.amdatu.web.dispatcher</Bundle-SymbolicName> + <Export-Package> + org.amdatu.web.dispatcher + </Export-Package> + <Embed-Dependency>*;scope=compile;inline=org/apache/felix/http/base/internal/util/**</Embed-Dependency> + <Embed-Transitive>true</Embed-Transitive> + </instructions> + </configuration> + </plugin> + </plugins> + </build> +</project> Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchExtenderFilter.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchExtenderFilter.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,7 @@ +package org.amdatu.web.dispatcher; + +import javax.servlet.Filter; + +public interface DispatchExtenderFilter extends Filter { + +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchFilterMatcher.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchFilterMatcher.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,9 @@ +package org.amdatu.web.dispatcher; + +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.web.dispatcher.handler.FilterHandler; + +public interface DispatchFilterMatcher { + boolean matches(FilterHandler filterHandler, HttpServletRequest httpServletRequest); +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchServletMatcher.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatchServletMatcher.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,9 @@ +package org.amdatu.web.dispatcher; + +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.web.dispatcher.handler.ServletHandler; + +public interface DispatchServletMatcher { + boolean matches(ServletHandler servletHandler, HttpServletRequest httpServletRequest); +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatcherService.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/DispatcherService.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,41 @@ +/* + Copyright (C) 2010 Amdatu.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package org.amdatu.web.dispatcher; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.web.dispatcher.context.HttpContextManager; +import org.amdatu.web.dispatcher.dispatch.FilterPipeline; +import org.osgi.service.log.LogService; + +public interface DispatcherService { + + + public final static String CONTEXT_ID_KEY = "contextId"; + public final static String PATTERN_KEY = "pattern"; + public final static String ALIAS_KEY = "alias"; + public final static String INIT_KEY_PREFIX = "init."; + +// HttpServlet getServletByAlias(String alias); + + public FilterPipeline getFilterPipeline(HttpServletRequest httpServletRequest); + + HttpContextManager getHttpContextManager(); + LogService getLogService(); + ServletContext getServletContext(); +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/DefaultHttpContext.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/DefaultHttpContext.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,31 @@ +package org.amdatu.web.dispatcher.context; + +import org.osgi.service.http.HttpContext; +import org.osgi.framework.Bundle; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.net.URL; + +public final class DefaultHttpContext implements HttpContext { + + private Bundle m_bundle; + + public DefaultHttpContext(Bundle bundle) { + m_bundle = bundle; + } + + public String getMimeType(String name) { + return null; + } + + public URL getResource(String name) { + if (name.startsWith("/")) { + name = name.substring(1); + } + return m_bundle.getResource(name); + } + + public boolean handleSecurity(HttpServletRequest req, HttpServletResponse res) { + return true; + } +} \ No newline at end of file Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/ExtServletContext.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/ExtServletContext.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,12 @@ +package org.amdatu.web.dispatcher.context; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public interface ExtServletContext extends ServletContext { + public boolean handleSecurity(HttpServletRequest req, HttpServletResponse res) + throws IOException; +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/HandlerServletContext.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/HandlerServletContext.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,147 @@ +package org.amdatu.web.dispatcher.context; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Set; + +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.felix.http.base.internal.util.MimeTypes; +import org.osgi.service.http.HttpContext; + +public class HandlerServletContext implements ExtServletContext { + + private final ServletContext m_extServletContext; + private final HttpContext m_httpContext; + + public HandlerServletContext(ServletContext m_extServletContext2, HttpContext httpContext) { + m_extServletContext = m_extServletContext2; + m_httpContext = httpContext; + } + + public Object getAttribute(String key) { + return m_extServletContext.getAttribute(key); + } + + public Enumeration getAttributeNames() { + return m_extServletContext.getAttributeNames(); + } + + public ServletContext getContext(String uri) { + return m_extServletContext.getContext(uri); + } + + public String getContextPath() { + return m_extServletContext.getContextPath(); + } + + public String getInitParameter(String key) { + return m_extServletContext.getInitParameter(key); + } + + public Enumeration getInitParameterNames() { + return m_extServletContext.getInitParameterNames(); + } + + public int getMajorVersion() { + return m_extServletContext.getMajorVersion(); + } + + public String getMimeType(String file) { + String type = m_httpContext.getMimeType(file); + if (type != null) { + return type; + } + return MimeTypes.get().getByFile(file); + } + + public int getMinorVersion() { + return m_extServletContext.getMinorVersion(); + } + + public RequestDispatcher getNamedDispatcher(String path) { + return m_extServletContext.getNamedDispatcher(path); + } + + public String getRealPath(String path) { + return m_extServletContext.getRealPath(path); + } + + public RequestDispatcher getRequestDispatcher(String uri) { + return m_extServletContext.getRequestDispatcher(uri); + } + + public URL getResource(String path) { + return m_httpContext.getResource(path); + + } + + public InputStream getResourceAsStream(String path) { + URL res = getResource(path); + if (res != null) { + try { + return res.openStream(); + } + catch (IOException e) {} + } + return null; + } + + public Set getResourcePaths(String path) { + return m_extServletContext.getResourcePaths(path); + } + + public String getServerInfo() { + return m_extServletContext.getServerInfo(); + } + + @SuppressWarnings("deprecation") + public Servlet getServlet(String name) throws ServletException { + return m_extServletContext.getServlet(name); + } + + public String getServletContextName() { + return m_extServletContext.getServletContextName(); + } + + @SuppressWarnings("deprecation") + public Enumeration getServletNames() { + return m_extServletContext.getServletNames(); + } + + @SuppressWarnings("deprecation") + public Enumeration getServlets() { + return m_extServletContext.getServlets(); + } + + public void log(String message) { + m_extServletContext.log(message); + } + + public void log(Exception exception, String message) { + m_extServletContext.log(exception, message); + } + + public void log(String message, Throwable cause) { + m_extServletContext.log(message, cause); + } + + public void removeAttribute(String key) { + m_extServletContext.removeAttribute(key); + } + + public void setAttribute(String key, Object value) { + m_extServletContext.setAttribute(key, value); + } + + public boolean handleSecurity(HttpServletRequest arg0, HttpServletResponse arg1) throws IOException { + return m_httpContext.handleSecurity(arg0, arg1); + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/HttpContextManager.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/context/HttpContextManager.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,44 @@ +package org.amdatu.web.dispatcher.context; + +import java.util.HashMap; +import java.util.Map; + +import org.osgi.framework.Bundle; +import org.osgi.service.http.HttpContext; + +public final class HttpContextManager { + + private final Map<String, HttpContext> m_idHttpContexts = new HashMap<String, HttpContext>(); + private final Map<HttpContext, String> m_httpContextIds = new HashMap<HttpContext, String>(); + + public HttpContextManager() { + } + + public synchronized HttpContext getHttpContext(Bundle bundle, String contextId) { + String id = createId(bundle, contextId); + HttpContext context = m_idHttpContexts.get(id); + if (context == null) { + context = new DefaultHttpContext(bundle); + m_idHttpContexts.put(id, context); + m_httpContextIds.put(context, id); + } + return context; + } + + public synchronized void removeHttpContext(HttpContext context) { + String id = m_httpContextIds.remove(context); + if (id != null) { + m_idHttpContexts.remove(id); + } + } + + public synchronized void addHttpContext(Bundle bundle, String contextId, HttpContext context) { + String id = createId(bundle, contextId); + m_idHttpContexts.put(id, context); + m_httpContextIds.put(context, id); + } + + private String createId(Bundle bundle, String contextId) { + return bundle.getBundleId() + "-" + contextId; + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/FilterPipeline.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/FilterPipeline.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,44 @@ +package org.amdatu.web.dispatcher.dispatch; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; + +import org.amdatu.web.dispatcher.handler.FilterHandler; + +public final class FilterPipeline { + + private final FilterHandler[] m_filterHandlers; + private final ServletPipeline m_servletPipeline; + + public FilterPipeline(FilterHandler[] handlers, ServletPipeline servletPipeline) { + m_filterHandlers = handlers; + m_servletPipeline = servletPipeline; + } + + public void dispatch(HttpServletRequest req, HttpServletResponse res, FilterChain proceedingChain) + throws ServletException, IOException { + FilterChain chain = new InvocationFilterChain(m_filterHandlers, m_servletPipeline, proceedingChain); + if (m_servletPipeline.hasServletsMapped()) { + req = new RequestWrapper(req); + } + chain.doFilter(req, res); + } + + private final class RequestWrapper extends HttpServletRequestWrapper { + public RequestWrapper(HttpServletRequest req) { + super(req); + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + final RequestDispatcher dispatcher = m_servletPipeline.getRequestDispatcher(path); + return (null != dispatcher) ? dispatcher : super.getRequestDispatcher(path); + } + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/HttpFilterChain.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/HttpFilterChain.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,20 @@ +package org.amdatu.web.dispatcher.dispatch; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public abstract class HttpFilterChain implements FilterChain { + public final void doFilter(ServletRequest req, ServletResponse res) + throws IOException, ServletException { + doFilter((HttpServletRequest) req, (HttpServletResponse) res); + } + + protected abstract void doFilter(HttpServletRequest req, HttpServletResponse res) + throws IOException, ServletException; +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/InvocationFilterChain.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/InvocationFilterChain.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,39 @@ +package org.amdatu.web.dispatcher.dispatch; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.amdatu.web.dispatcher.handler.FilterHandler; + +public final class InvocationFilterChain + extends HttpFilterChain { + private final FilterHandler[] handlers; + private final ServletPipeline servletPipeline; + private final FilterChain proceedingChain; + private int index = -1; + + public InvocationFilterChain(FilterHandler[] handlers, ServletPipeline servletPipeline, FilterChain proceedingChain) { + this.handlers = handlers; + this.servletPipeline = servletPipeline; + this.proceedingChain = proceedingChain; + } + + 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.servletPipeline.handle(req, res)) { + System.err.println("Request not handled. Returning to parent chain... "); + this.proceedingChain.doFilter(req, res); + } + } + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/NotFoundFilterChain.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/NotFoundFilterChain.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,15 @@ +package org.amdatu.web.dispatcher.dispatch; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public final class NotFoundFilterChain extends HttpFilterChain { + + protected void doFilter(HttpServletRequest req, HttpServletResponse res) + throws IOException, ServletException { + res.sendError(HttpServletResponse.SC_NOT_FOUND); + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/ServletPipeline.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/dispatch/ServletPipeline.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,80 @@ +package org.amdatu.web.dispatcher.dispatch; + +import java.io.IOException; + +import javax.servlet.RequestDispatcher; +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.amdatu.web.dispatcher.handler.ServletHandler; + +public final class ServletPipeline { + + private final ServletHandler[] m_servletHandlers; + + public ServletPipeline(ServletHandler[] handlers) { + m_servletHandlers = handlers; + } + + public boolean handle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { + for (ServletHandler handler : m_servletHandlers) { + if (handler.handle(req, res)) { + return true; + } + } + return false; + } + + public boolean hasServletsMapped() { + return m_servletHandlers.length > 0; + } + + public RequestDispatcher getRequestDispatcher(String path) { + for (ServletHandler handler : m_servletHandlers) { + if (handler.matchesPath(path)) { + return new Dispatcher(path, handler); + } + } + return null; + } + + private final class Dispatcher implements RequestDispatcher { + + private final String m_path; + private final ServletHandler m_servletHandler; + + public Dispatcher(String path, ServletHandler handler) { + m_path = path; + m_servletHandler = handler; + } + + public void forward(ServletRequest req, ServletResponse res) throws ServletException, IOException { + if (res.isCommitted()) { + throw new ServletException("Response has been committed"); + } + m_servletHandler.handle(new RequestWrapper((HttpServletRequest) req, m_path), (HttpServletResponse) res); + } + + public void include(ServletRequest req, ServletResponse res) throws ServletException, IOException { + m_servletHandler.handle((HttpServletRequest) req, (HttpServletResponse) res); + } + } + + private final class RequestWrapper extends HttpServletRequestWrapper { + + private final String m_requestUri; + + public RequestWrapper(HttpServletRequest req, String requestUri) { + super(req); + m_requestUri = requestUri; + } + + public String getRequestURI() { + return m_requestUri; + } + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/extender/TenantParameterDispatchExtender.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/extender/TenantParameterDispatchExtender.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,51 @@ +package org.amdatu.web.dispatcher.extender; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.core.tenant.Tenant; +import org.amdatu.core.tenant.TenantException; +import org.amdatu.core.tenant.TenantManagementService; +import org.amdatu.web.dispatcher.DispatchExtenderFilter; + +public final class TenantParameterDispatchExtender implements DispatchExtenderFilter { + + public final static String TENANT_REQUESTCONTEXT_KEY = "org.amdatu.web.dispatcher.TENANT"; + public final static String TENANT_REQUEST_PARAMETER = "tenant"; + + private TenantManagementService m_tenantManagementService; + + public void setTenentManagementService(TenantManagementService tenantManagementService) { + m_tenantManagementService = tenantManagementService; + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, + ServletException { + + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String tenantId = httpServletRequest.getParameter(TENANT_REQUEST_PARAMETER); + if (tenantId != null) { + try { + Tenant tenant = m_tenantManagementService.getTenantById(tenantId); + if (tenant != null) { + servletRequest.setAttribute(TENANT_REQUESTCONTEXT_KEY, tenant); + } + } + catch (TenantException e) {} + } + filterChain.doFilter(servletRequest, servletResponse); + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/filter/DispatchInterceptFilter.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/filter/DispatchInterceptFilter.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,103 @@ +package org.amdatu.web.dispatcher.filter; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +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.HttpServletResponse; + +import org.amdatu.web.dispatcher.DispatcherService; +import org.amdatu.web.dispatcher.dispatch.FilterPipeline; +import org.osgi.service.log.LogService; + +/** + * Simple interceptor filter that collaborates with <code>DispatcherService</code> + * to redirect request to a custom (mostly tenant specific) pipeline. When there + * is no custom pipeline or no target matches the request is continued down the + * original chain. + */ +public final class DispatchInterceptFilter implements Filter { + + private DispatcherService m_dispatcherService; + private LogService m_logService; + private FilterConfig m_filterConfig; + private boolean m_initialized = false; + + public void setDispatcherService(DispatcherService dispatcherService) { + m_dispatcherService = dispatcherService; + } + + public void setLogService(LogService logService) { + m_logService = logService; + } + + public ServletContext getServletContext() { + return m_filterConfig.getServletContext(); + } + + public void init(FilterConfig filterConfig) throws ServletException { + if (m_initialized) + throw new IllegalStateException(DispatchInterceptFilter.class.getSimpleName() + " duplicate init call"); + m_initialized = true; + m_filterConfig = filterConfig; + if (m_logService != null) + m_logService.log(LogService.LOG_INFO, DispatchInterceptFilter.class.getSimpleName() + " initialized"); + } + + public void destroy() { + if (m_logService != null) + m_logService.log(LogService.LOG_INFO, DispatchInterceptFilter.class.getSimpleName() + " destroyed"); + } + + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, + ServletException { + + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + + // FIXME: no excessive logging in request path + if (m_logService != null) + m_logService.log(LogService.LOG_WARNING, getLogMessage(httpServletRequest)); + + FilterPipeline filterPipeline = m_dispatcherService.getFilterPipeline(httpServletRequest); + if (filterPipeline != null) { + + // Standard behavior: invoke the custom chain that falls + // back to the normal http service chain when no servlet + // handles the request. + filterPipeline.dispatch(httpServletRequest, httpServletResponse, + filterChain); + return; + } + + // Fallback behavior: if we have no custom chain we just + // continue the normal http service chain. + filterChain.doFilter(servletRequest, servletResponse); + } + + private String getLogMessage(HttpServletRequest httpServletRequest) { + StringBuilder sb = new StringBuilder("Intercepted request: "); + sb.append(httpServletRequest.getScheme()); + sb.append("://"); + sb.append(httpServletRequest.getServerName()); + if (httpServletRequest.getServerPort() != -1 && httpServletRequest.getServerPort() != 80) { + sb.append(":"); + sb.append(httpServletRequest.getServerPort()); + } + if (httpServletRequest.getPathInfo() != null) { + sb.append(httpServletRequest.getPathInfo()); + } + if (httpServletRequest.getQueryString() != null) { + sb.append("?"); + sb.append(httpServletRequest.getQueryString()); + } + return sb.toString(); + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/AbstractHandler.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/AbstractHandler.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,64 @@ +package org.amdatu.web.dispatcher.handler; + +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.servlet.ServletException; + +import org.amdatu.web.dispatcher.context.ExtServletContext; + +public abstract class AbstractHandler { + + private final static AtomicInteger ID = + new AtomicInteger(); + + private final String m_handlerId; + private final ExtServletContext m_servletContext; + private final Map<String, String> m_initParams; + private final String m_tenantid; + + public AbstractHandler(ExtServletContext context, String tenantid) { + m_handlerId = "" + ID.incrementAndGet(); + m_servletContext = context; + m_initParams = new HashMap<String, String>(); + m_tenantid = tenantid; + } + + public final String getId() { + return m_handlerId; + } + + public final String getTenantId() { + return m_tenantid; + } + + protected final ExtServletContext getContext() { + return m_servletContext; + } + + public final Map<String, String> getInitParams() { + return m_initParams; + } + + public final void setInitParams(Dictionary<String, String> map) { + m_initParams.clear(); + if (map == null) { + return; + } + Enumeration<String> e = map.keys(); + while (e.hasMoreElements()) { + Object key = e.nextElement(); + Object value = map.get(key); + if ((key instanceof String) && (value instanceof String)) { + this.m_initParams.put((String) key, (String) value); + } + } + } + + public abstract void init() throws ServletException; + + public abstract void destroy(); +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/AbstractHandlerRegistry.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/AbstractHandlerRegistry.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,78 @@ +package org.amdatu.web.dispatcher.handler; + +import java.util.Dictionary; +import java.util.Hashtable; + +import javax.servlet.ServletContext; + +import org.amdatu.web.dispatcher.DispatcherService; +import org.amdatu.web.dispatcher.context.DefaultHttpContext; +import org.amdatu.web.dispatcher.service.DispatcherServiceImpl; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; +import org.osgi.service.http.HttpContext; +import org.osgi.service.log.LogService; + +public abstract class AbstractHandlerRegistry { + + private DispatcherService m_dispatcherService; + + public AbstractHandlerRegistry(DispatcherService dispatcherService) { + m_dispatcherService = dispatcherService; + } + + protected DispatcherService getDispatcherService() { + return m_dispatcherService; + } + + protected LogService getLogService() { + return m_dispatcherService.getLogService(); + } + + protected ServletContext getServletContext() { + return m_dispatcherService.getServletContext(); + } + + protected String getStringProperty(ServiceReference ref, String key) { + Object value = ref.getProperty(key); + return (value instanceof String) ? (String) value : null; + } + + protected int getIntProperty(ServiceReference ref, String key, int defValue) { + Object value = ref.getProperty(key); + if (value == null) { + return defValue; + } + try { + return Integer.parseInt(value.toString()); + } + catch (Exception e) { + return defValue; + } + } + + protected Dictionary<String, String> getInitParams(ServiceReference serviceReference) { + Dictionary<String, String> initParams = new Hashtable<String, String>(); + for (String key : serviceReference.getPropertyKeys()) { + if (key.startsWith(DispatcherServiceImpl.INIT_KEY_PREFIX)) { + String paramKey = key.substring(DispatcherServiceImpl.INIT_KEY_PREFIX.length()); + String paramValue = getStringProperty(serviceReference, key); + if (paramValue != null) { + initParams.put(paramKey, paramValue); + } + } + } + return initParams; + } + + protected HttpContext getHttpContext(ServiceReference serviceReference) { + Bundle bundle = serviceReference.getBundle(); + String contextId = getStringProperty(serviceReference, DispatcherServiceImpl.CONTEXT_ID_KEY); + if (contextId != null) { + return m_dispatcherService.getHttpContextManager().getHttpContext(bundle, contextId); + } + else { + return new DefaultHttpContext(bundle); + } + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterConfigImpl.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterConfigImpl.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,37 @@ +package org.amdatu.web.dispatcher.handler; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; + +public final class FilterConfigImpl implements FilterConfig { + + private final String m_name; + private final ServletContext m_servletcontext; + private final Map<String, String> m_initParams; + + public FilterConfigImpl(String name, ServletContext context, Map<String, String> initParams) { + m_name = name; + m_servletcontext = context; + m_initParams = initParams; + } + + public String getFilterName() { + return m_name; + } + + public ServletContext getServletContext() { + return m_servletcontext; + } + + public String getInitParameter(String name) { + return m_initParams.get(name); + } + + public Enumeration<String> getInitParameterNames() { + return Collections.enumeration(m_initParams.keySet()); + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterHandler.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterHandler.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,79 @@ +package org.amdatu.web.dispatcher.handler; + +import java.io.IOException; +import java.util.regex.Pattern; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.amdatu.web.dispatcher.context.ExtServletContext; + +public final class FilterHandler extends AbstractHandler implements Comparable<FilterHandler> { + + private final FilterHandlerRegistry m_filterHandlerRegistry; + private final Filter m_filter; + private final Pattern m_pattern; + private final int m_ranking; + + public FilterHandler(FilterHandlerRegistry filterHandlerRegistry, ExtServletContext context, Filter filter, + String pattern, int ranking, String tenantid) { + super(context, tenantid); + m_filterHandlerRegistry = filterHandlerRegistry; + m_filter = filter; + m_ranking = ranking; + m_pattern = Pattern.compile(pattern); + } + + public Filter getFilter() { + return m_filter; + } + + public void init() + throws ServletException { + String name = "filter_" + getId(); + FilterConfig config = new FilterConfigImpl(name, getContext(), getInitParams()); + m_filter.init(config); + } + + public void destroy() { + m_filter.destroy(); + } + + public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, + FilterChain filterChain) throws ServletException, IOException { + boolean matches = matchesPath(httpServletRequest.getPathInfo()); + if (matches) { + matches = m_filterHandlerRegistry.filterHandlerMatches(this, httpServletRequest); + if (matches) { + doHandle(httpServletRequest, httpServletResponse, filterChain); + return; + } + } + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + public boolean matchesPath(String uri) { + if (uri == null) { + uri = "/"; + } + return m_pattern.matcher(uri).matches(); + } + + public int compareTo(FilterHandler other) { + return other.m_ranking - m_ranking; + } + + private void doHandle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) + throws ServletException, IOException { + if (!getContext().handleSecurity(req, res)) { + res.sendError(HttpServletResponse.SC_FORBIDDEN); + } + else { + m_filter.doFilter(req, res, chain); + } + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterHandlerRegistry.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/FilterHandlerRegistry.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,119 @@ +package org.amdatu.web.dispatcher.handler; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.servlet.Filter; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.web.dispatcher.DispatchFilterMatcher; +import org.amdatu.web.dispatcher.DispatcherService; +import org.amdatu.web.dispatcher.context.HandlerServletContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; + +public final class FilterHandlerRegistry extends AbstractHandlerRegistry { + + private final Map<ServiceReference, FilterHandler> m_filterHandlers = + new HashMap<ServiceReference, FilterHandler>(); + + private final Map<Filter, FilterHandler> m_extenderFilterHandlers = new HashMap<Filter, FilterHandler>(); + private Set<DispatchFilterMatcher> m_matchers = new HashSet<DispatchFilterMatcher>(); + + public FilterHandlerRegistry(DispatcherService dispatcherService) { + super(dispatcherService); + } + + public void addDispatchFilterMatcher(DispatchFilterMatcher dispatchFilterMatcher) { + if (m_matchers.contains(dispatchFilterMatcher)) { + throw new IllegalStateException("unforseen duplicate matcher...."); + } + m_matchers.add(dispatchFilterMatcher); + } + + public void removeDispatchFilterMatcher(DispatchFilterMatcher dispatchFilterMatcher) { + if (!m_matchers.contains(dispatchFilterMatcher)) { + throw new IllegalStateException("unforseen unknown matcher...."); + } + m_matchers.remove(dispatchFilterMatcher); + } + + public void addDispatchExtenderFilter(FilterHandler handler) + throws ServletException { + if (m_extenderFilterHandlers.containsKey(handler.getFilter())) { + throw new IllegalStateException("unforseen duplicate extender...."); + } + + handler.init(); + m_extenderFilterHandlers.put(handler.getFilter(), handler); + } + + public void removeDispatchExtenderFilter(Filter filter) { + if (!m_extenderFilterHandlers.containsKey(filter)) { + throw new IllegalStateException("unforseen unknown extender...."); + } + FilterHandler handler = m_extenderFilterHandlers.remove(filter); + if (handler != null) { + handler.destroy(); + } + } + + public void addFilterHandler(ServiceReference serviceReference, Filter filter) { + if (m_filterHandlers.containsKey(serviceReference)) { + throw new IllegalStateException("Unexpected.... "); + } + int ranking = getIntProperty(serviceReference, Constants.SERVICE_RANKING, 0); + String pattern = getStringProperty(serviceReference, DispatcherService.PATTERN_KEY); + if (pattern == null) { + return; + } + + String tenant = getStringProperty(serviceReference, "tenant_id"); + + HandlerServletContext extServletContextWrapper = + new HandlerServletContext(getServletContext(), getHttpContext(serviceReference)); + FilterHandler filterHandler = + new FilterHandler(this, extServletContextWrapper, filter, pattern, ranking, tenant); + filterHandler.setInitParams(getInitParams(serviceReference)); + + m_filterHandlers.put(serviceReference, filterHandler); + } + + public void removeFilterHandler(ServiceReference serviceReference, Filter filter) { + if (!m_filterHandlers.containsKey(serviceReference)) { + throw new IllegalStateException("Unexpected.... "); + } + FilterHandler filterHandler = m_filterHandlers.remove(serviceReference); + if (filterHandler != null) { + filterHandler.destroy(); + } + } + + public FilterHandler[] getFilterHandlers(HttpServletRequest httpServletRequest) { + // FIXME optimize + FilterHandler[] filterHandlers = m_filterHandlers.values().toArray(new FilterHandler[m_filterHandlers.size()]); + Arrays.sort(filterHandlers); + FilterHandler[] extenderFilterHandlers = + m_extenderFilterHandlers.values().toArray(new FilterHandler[m_extenderFilterHandlers.size()]); + Arrays.sort(filterHandlers); + FilterHandler[] finalFilterHandlers = new FilterHandler[filterHandlers.length + extenderFilterHandlers.length]; + System.arraycopy(extenderFilterHandlers, 0, finalFilterHandlers, 0, extenderFilterHandlers.length); + System.arraycopy(filterHandlers, 0, finalFilterHandlers, extenderFilterHandlers.length, filterHandlers.length); + + System.err.println("Number of matching filters: " + filterHandlers.length); + return finalFilterHandlers; + } + + public boolean filterHandlerMatches(FilterHandler filterHandler, HttpServletRequest httpServletRequest) { + for (DispatchFilterMatcher matcher : m_matchers) { + if (!matcher.matches(filterHandler, httpServletRequest)) { + return false; + } + } + return true; + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletConfigImpl.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletConfigImpl.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,37 @@ +package org.amdatu.web.dispatcher.handler; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; + +public final class ServletConfigImpl implements ServletConfig { + + private final String m_name; + private final ServletContext m_servletContext; + private final Map<String, String> m_initParams; + + public ServletConfigImpl(String name, ServletContext context, Map<String, String> initParams) { + m_name = name; + m_servletContext = context; + m_initParams = initParams; + } + + public String getServletName() { + return m_name; + } + + public ServletContext getServletContext() { + return m_servletContext; + } + + public String getInitParameter(String name) { + return m_initParams.get(name); + } + + public Enumeration<String> getInitParameterNames() { + return Collections.enumeration(m_initParams.keySet()); + } +} \ No newline at end of file Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandler.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandler.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,85 @@ +package org.amdatu.web.dispatcher.handler; + +import java.io.IOException; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.amdatu.web.dispatcher.context.ExtServletContext; + +public final class ServletHandler extends AbstractHandler implements Comparable<ServletHandler> { + + private final ServletHandlerRegistry m_servletHandlerRegistry; + private final String m_alias; + private final Servlet m_servlet; + + public ServletHandler(ServletHandlerRegistry servletHandlerRegistry, ExtServletContext context, Servlet servlet, + String alias, String tenantid) { + super(context, tenantid); + m_servletHandlerRegistry = servletHandlerRegistry; + m_alias = alias; + m_servlet = servlet; + } + + public String getAlias() { + return m_alias; + } + + public Servlet getServlet() { + return m_servlet; + } + + public void init() throws ServletException { + String name = "servlet_" + getId(); + ServletConfig config = new ServletConfigImpl(name, getContext(), getInitParams()); + m_servlet.init(config); + } + + public void destroy() { + this.m_servlet.destroy(); + } + + public boolean handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) + throws ServletException, IOException { + + boolean matches = matchesPath(httpServletRequest.getPathInfo()); + if (matches) { + if (m_servletHandlerRegistry.servletHandlerMatches(this, httpServletRequest)) { + doHandle(httpServletRequest, httpServletResponse); + return true; + } + } + return false; + } + + public boolean matchesPath(String uri) { + if (uri == null) { + return m_alias.equals("/"); + } + else if (m_alias.equals("/")) { + return uri.startsWith(m_alias); + } + else { + return uri.equals(m_alias) || uri.startsWith(m_alias + "/"); + } + } + + public int compareTo(ServletHandler other) { + return other.m_alias.length() - m_alias.length(); + } + + private void doHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) + throws ServletException, IOException { + if (!getContext().handleSecurity(httpServletRequest, httpServletResponse)) { + if (!httpServletResponse.isCommitted()) { + httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN); + } + } + else { + m_servlet.service(new ServletHandlerRequest(httpServletRequest, m_alias), httpServletResponse); + } + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandlerRegistry.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandlerRegistry.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,95 @@ +package org.amdatu.web.dispatcher.handler; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.servlet.Servlet; +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.web.dispatcher.DispatchServletMatcher; +import org.amdatu.web.dispatcher.DispatcherService; +import org.amdatu.web.dispatcher.context.HandlerServletContext; +import org.amdatu.web.dispatcher.service.DispatcherServiceImpl; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogService; + +//FIXME synchronization +public final class ServletHandlerRegistry extends AbstractHandlerRegistry { + + private final Map<ServiceReference, ServletHandler> m_servletHandlers = + new HashMap<ServiceReference, ServletHandler>(); + + private Set<DispatchServletMatcher> m_matchers = new HashSet<DispatchServletMatcher>(); + + public ServletHandlerRegistry(DispatcherService dispatcherService) { + super(dispatcherService); + } + + public void addDispatchServletMatcher(DispatchServletMatcher dispatchServletMatcher) { + if (m_matchers.contains(dispatchServletMatcher)) { + throw new IllegalStateException("unforseen duplicate matcher...."); + } + m_matchers.add(dispatchServletMatcher); + } + + public void removeDispatchServletMatcher(DispatchServletMatcher dispatchServletMatcher) { + if (!m_matchers.contains(dispatchServletMatcher)) { + throw new IllegalStateException("unforseen unknown matcher...."); + } + m_matchers.remove(dispatchServletMatcher); + } + + public void addServletHandler(ServiceReference serviceReference, Servlet servlet) { + if (m_servletHandlers.containsKey(serviceReference)) { + throw new IllegalStateException("Unexpected.... "); + } + + String alias = getStringProperty(serviceReference, DispatcherServiceImpl.ALIAS_KEY); + if (alias == null) { + if (getLogService() != null) + getLogService().log(LogService.LOG_WARNING, "Cannot register a servlet without alias"); + return; + } + + String tenant = getStringProperty(serviceReference, "tenant_id"); + + HandlerServletContext servletContextWrapper = + new HandlerServletContext(getServletContext(), getHttpContext(serviceReference)); + + ServletHandler handler = new ServletHandler(this, servletContextWrapper, servlet, alias, tenant); + handler.setInitParams(getInitParams(serviceReference)); + + m_servletHandlers.put(serviceReference, handler); + } + + public void removeServletHandler(ServiceReference serviceReference, Servlet servlet) { + if (!m_servletHandlers.containsKey(serviceReference)) { + return; + } + ServletHandler servletHandler = m_servletHandlers.remove(serviceReference); + if (servletHandler != null) { + servletHandler.destroy(); + } + } + + public ServletHandler[] getServletHandlers(HttpServletRequest httpServletRequest) { + ServletHandler[] servletHandlers = + m_servletHandlers.values().toArray(new ServletHandler[m_servletHandlers.size()]); + Arrays.sort(servletHandlers); + + System.err.println("Number of matching servlets: " + servletHandlers.length); + return servletHandlers; + } + + public boolean servletHandlerMatches(ServletHandler servletHandler, HttpServletRequest httpServletRequest) { + for (DispatchServletMatcher matcher : m_matchers) { + if (!matcher.matches(servletHandler, httpServletRequest)) { + return false; + } + } + return true; + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandlerRequest.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/handler/ServletHandlerRequest.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,91 @@ +package org.amdatu.web.dispatcher.handler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import org.osgi.service.http.HttpContext; + +final class ServletHandlerRequest extends HttpServletRequestWrapper { + + private final String m_alias; + private String m_contextPath; + private String m_pathInfo; + private boolean m_pathInfoCalculated = false; + + public ServletHandlerRequest(HttpServletRequest req, String alias) { + super(req); + m_alias = alias; + } + + @Override + public String getAuthType() { + String authType = (String) getAttribute(HttpContext.AUTHENTICATION_TYPE); + if (authType != null) { + return authType; + } + return super.getAuthType(); + } + + @Override + public String getContextPath() { + if (m_contextPath == null) { + final String context = super.getContextPath(); + final String servlet = super.getServletPath(); + if (context.length() == 0) { + m_contextPath = servlet; + } + else if (servlet.length() == 0) { + m_contextPath = context; + } + else { + m_contextPath = context + servlet; + } + } + return m_contextPath; + } + + @Override + public String getPathInfo() { + if (!m_pathInfoCalculated) { + m_pathInfo = calculatePathInfo(); + m_pathInfoCalculated = true; + } + return m_pathInfo; + } + + @Override + 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 String getServletPath() { + if ("/".equals(m_alias)) { + return ""; + } + return m_alias; + } + + private String calculatePathInfo() { + String pathInfo = super.getPathInfo(); + if (pathInfo != null) { + if (!"/".equals(m_alias)) { + pathInfo = pathInfo.substring(m_alias.length()); + } + if (pathInfo.length() == 0) { + pathInfo = null; + } + } + return pathInfo; + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/matcher/TenantAwareServletMatcher.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/matcher/TenantAwareServletMatcher.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,20 @@ +package org.amdatu.web.dispatcher.matcher; + +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.core.tenant.Tenant; +import org.amdatu.web.dispatcher.DispatchServletMatcher; +import org.amdatu.web.dispatcher.extender.TenantParameterDispatchExtender; +import org.amdatu.web.dispatcher.handler.ServletHandler; + +public class TenantAwareServletMatcher implements DispatchServletMatcher { + + public boolean matches(ServletHandler servletHandler, HttpServletRequest httpServletRequest) { + if (servletHandler.getTenantId() == null || servletHandler.getTenantId() == "") { + return true; + } + Tenant tenant = + (Tenant) httpServletRequest.getAttribute(TenantParameterDispatchExtender.TENANT_REQUESTCONTEXT_KEY); + return (tenant != null && servletHandler.getTenantId().equals(tenant.getId())); + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/matcher/TenantUnawareServletMatcher.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/matcher/TenantUnawareServletMatcher.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,13 @@ +package org.amdatu.web.dispatcher.matcher; + +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.web.dispatcher.DispatchServletMatcher; +import org.amdatu.web.dispatcher.handler.ServletHandler; + +public class TenantUnawareServletMatcher implements DispatchServletMatcher { + + public boolean matches(ServletHandler servletHandler, HttpServletRequest httpServletRequest) { + return (servletHandler.getTenantId() == null || servletHandler.getTenantId().equals("")); + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/osgi/DispatcherServiceActivator.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/osgi/DispatcherServiceActivator.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,79 @@ +/* + Copyright (C) 2010 Amdatu.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package org.amdatu.web.dispatcher.osgi; + +import java.util.Dictionary; +import java.util.Hashtable; + +import javax.servlet.Filter; +import javax.servlet.Servlet; + +import org.amdatu.core.tenant.Tenant; +import org.amdatu.core.tenant.TenantManagementService; +import org.amdatu.web.dispatcher.DispatcherService; +import org.amdatu.web.dispatcher.service.DispatcherServiceImpl; +import org.amdatu.web.dispatcher.test.HelloWorldServlet; +import org.apache.felix.dm.DependencyActivatorBase; +import org.apache.felix.dm.DependencyManager; +import org.apache.felix.http.api.ExtHttpService; +import org.osgi.framework.BundleContext; +import org.osgi.service.http.HttpContext; +import org.osgi.service.log.LogService; + +public final class DispatcherServiceActivator extends DependencyActivatorBase { + + @Override + public void init(BundleContext context, DependencyManager manager) throws Exception { + + Dictionary<String, Object> serviceProperties = new Hashtable<String, Object>(); + manager.add( + createComponent() + .setInterface(DispatcherService.class.getName(), serviceProperties) + .setImplementation(DispatcherServiceImpl.class) + .add(createServiceDependency().setService(ExtHttpService.class).setRequired(true)) + .add(createServiceDependency().setService(LogService.class).setRequired(true)) + .add(createServiceDependency().setService(TenantManagementService.class).setRequired(true)) + .add( + createServiceDependency() + .setService(HttpContext.class) + .setCallbacks("addHttpContext", "removeHttpContext") + .setRequired(false)) + .add( + createServiceDependency() + .setService(Servlet.class) + .setCallbacks("addServlet", "removeServlet") + .setRequired(false)) + .add( + createServiceDependency() + .setService(Filter.class) + .setCallbacks("addFilter", "removeFilter") + .setRequired(false)) + ); + + // test servlets + Dictionary<String, String> props = new Hashtable<String, String>(); + props.put("alias", "/hello/world"); + manager.add( + createAdapterService(Tenant.class, null) + .setImplementation(HelloWorldServlet.class) + .setInterface(Servlet.class.getName(), props)); + } + + @Override + public void destroy(BundleContext context, DependencyManager manager) throws Exception { + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/service/DispatcherServiceImpl.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/service/DispatcherServiceImpl.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,176 @@ +/* + Copyright (C) 2010 Amdatu.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package org.amdatu.web.dispatcher.service; + +import java.util.Hashtable; + +import javax.servlet.Filter; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import org.amdatu.core.tenant.TenantManagementService; +import org.amdatu.web.dispatcher.DispatcherService; +import org.amdatu.web.dispatcher.context.DefaultHttpContext; +import org.amdatu.web.dispatcher.context.HandlerServletContext; +import org.amdatu.web.dispatcher.context.HttpContextManager; +import org.amdatu.web.dispatcher.dispatch.FilterPipeline; +import org.amdatu.web.dispatcher.dispatch.ServletPipeline; +import org.amdatu.web.dispatcher.extender.TenantParameterDispatchExtender; +import org.amdatu.web.dispatcher.filter.DispatchInterceptFilter; +import org.amdatu.web.dispatcher.handler.FilterHandler; +import org.amdatu.web.dispatcher.handler.FilterHandlerRegistry; +import org.amdatu.web.dispatcher.handler.ServletHandlerRegistry; +import org.amdatu.web.dispatcher.matcher.TenantAwareServletMatcher; +import org.apache.felix.http.api.ExtHttpService; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.http.HttpContext; +import org.osgi.service.log.LogService; + +public class DispatcherServiceImpl implements DispatcherService { + + private volatile BundleContext m_bundleContext; + private volatile LogService m_logService; + private volatile ExtHttpService m_httpService; + private volatile TenantManagementService m_tenantManagementService; + + private DispatchInterceptFilter m_interceptorFilter; + private HttpContextManager m_httpContextManager; + private ServletHandlerRegistry m_servletHandlerRegistry; + private FilterHandlerRegistry m_filterHandlerRegistry; + + private ServletContext m_extServletContext; + + public void init() { + if (m_logService != null) + m_logService.log(LogService.LOG_INFO, DispatcherServiceImpl.class.getSimpleName() + " initialized"); + } + + public void destroy() { + if (m_logService != null) + m_logService.log(LogService.LOG_INFO, DispatcherServiceImpl.class.getSimpleName() + " destroyed"); + } + + public void start() throws Exception { + + // tmp should be service regs + m_servletHandlerRegistry = new ServletHandlerRegistry(this); + m_servletHandlerRegistry.addDispatchServletMatcher(new TenantAwareServletMatcher()); + + // tmp should be service regs + m_filterHandlerRegistry = new FilterHandlerRegistry(this); + HandlerServletContext extServletContextWrapper = + new HandlerServletContext(m_extServletContext, new DefaultHttpContext(m_bundleContext.getBundle())); + TenantParameterDispatchExtender tde = new TenantParameterDispatchExtender(); + tde.setTenentManagementService(m_tenantManagementService); + FilterHandler handler = + new FilterHandler(m_filterHandlerRegistry, extServletContextWrapper, tde, ".*", 0, null); + handler.setInitParams(null); + m_filterHandlerRegistry.addDispatchExtenderFilter(handler); + + m_httpContextManager = new HttpContextManager(); + + m_interceptorFilter = new DispatchInterceptFilter(); + m_interceptorFilter.setLogService(m_logService); + m_interceptorFilter.setDispatcherService(this); + try { + m_httpService.registerFilter(m_interceptorFilter, ".*", new Hashtable<String, Object>(), 0, + new DefaultHttpContext(m_bundleContext.getBundle())); + m_extServletContext = m_interceptorFilter.getServletContext(); + } + catch (ServletException e) { + throw new Exception(DispatcherServiceImpl.class.getSimpleName() + " failed to start", e); + } + m_logService.log(LogService.LOG_INFO, DispatcherServiceImpl.class.getSimpleName() + " started"); + } + + public void stop() { + m_logService.log(LogService.LOG_ERROR, "stop"); + + m_httpContextManager = null; + + m_httpService.unregister(".*"); + m_interceptorFilter.setDispatcherService(null); + m_interceptorFilter.setLogService(null); + m_interceptorFilter = null; + + m_logService.log(LogService.LOG_INFO, DispatcherServiceImpl.class.getSimpleName() + " stopped"); + } + + public void addHttpContext(ServiceReference serviceReference, Object service) { + HttpContext httpContext = (HttpContext) service; + Bundle bundle = serviceReference.getBundle(); + String contextId = getStringProperty(serviceReference, CONTEXT_ID_KEY); + if (contextId != null) { + m_httpContextManager.addHttpContext(bundle, contextId, httpContext); + } + } + + public void removeHttpContext(ServiceReference serviceReference, Object service) { + HttpContext httpContext = (HttpContext) service; + m_httpContextManager.removeHttpContext(httpContext); + } + + public void addServlet(ServiceReference serviceReference, Object service) { + Servlet servlet = (Servlet) service; + m_servletHandlerRegistry.addServletHandler(serviceReference, servlet); + } + + public void removeServlet(ServiceReference serviceReference, Object service) { + Servlet servlet = (Servlet) service; + m_servletHandlerRegistry.removeServletHandler(serviceReference, servlet); + } + + public void addFilter(ServiceReference serviceReference, Object service) { + Filter filter = (Filter) service; + m_filterHandlerRegistry.addFilterHandler(serviceReference, filter); + } + + public void removeFilter(ServiceReference serviceReference, Object service) { + Filter filter = (Filter) service; + m_filterHandlerRegistry.removeFilterHandler(serviceReference, filter); + } + + public FilterPipeline getFilterPipeline(HttpServletRequest httpServletRequest) { + ServletPipeline servletPipeline = + new ServletPipeline(m_servletHandlerRegistry.getServletHandlers(httpServletRequest)); + FilterPipeline filterPipeline = + new FilterPipeline(m_filterHandlerRegistry.getFilterHandlers(httpServletRequest), + servletPipeline); + return filterPipeline; + } + + private String getStringProperty(ServiceReference ref, String key) { + Object value = ref.getProperty(key); + return (value instanceof String) ? (String) value : null; + } + + public HttpContextManager getHttpContextManager() { + return m_httpContextManager; + } + + public LogService getLogService() { + return m_logService; + } + + public ServletContext getServletContext() { + return m_extServletContext; + } +} Added: sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/test/HelloWorldServlet.java ============================================================================== --- (empty file) +++ sandbox/bdekruijff/dispatcher/src/main/java/org/amdatu/web/dispatcher/test/HelloWorldServlet.java Tue Jan 25 14:25:33 2011 @@ -0,0 +1,47 @@ +package org.amdatu.web.dispatcher.test; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletResponse; + +import org.amdatu.core.tenant.Tenant; + +public class HelloWorldServlet extends HttpServlet { + + private Tenant m_tenant; + + public void destroy() { + // TODO Auto-generated method stub + + } + + public ServletConfig getServletConfig() { + // TODO Auto-generated method stub + return null; + } + + public String getServletInfo() { + // TODO Auto-generated method stub + return null; + } + + public void init(ServletConfig arg0) throws ServletException { + // TODO Auto-generated method stub + + } + + public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, + IOException { + // default behaviour + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + PrintWriter writer = httpServletResponse.getWriter(); + writer.append("Hello World by tenant " + m_tenant.getName()); + writer.close(); + } +}
