FELIX-1456: Added improved httpservice implementation

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@814549 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/LICENSE b/http/LICENSE
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/http/LICENSE
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/http/README b/http/README
new file mode 100644
index 0000000..daf8b86
--- /dev/null
+++ b/http/README
@@ -0,0 +1,2 @@
+Extended OSGi HttpService
+-------------------------
diff --git a/http/api/pom.xml b/http/api/pom.xml
new file mode 100644
index 0000000..739cc31
--- /dev/null
+++ b/http/api/pom.xml
@@ -0,0 +1,47 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Api</name>
+    <artifactId>org.apache.felix.http.api</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/api/src/main/java/org/apache/felix/http/api/ExtHttpService.java b/http/api/src/main/java/org/apache/felix/http/api/ExtHttpService.java
new file mode 100644
index 0000000..2d15411
--- /dev/null
+++ b/http/api/src/main/java/org/apache/felix/http/api/ExtHttpService.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.api;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.HttpContext;
+import java.util.Dictionary;
+
+public interface ExtHttpService
+    extends HttpService
+{
+    public void registerFilter(Filter filter, String pattern, Dictionary initParams, int ranking, HttpContext context)
+        throws ServletException;
+    
+    public void unregisterFilter(Filter filter);
+
+    public void unregisterServlet(Servlet servlet);
+}
diff --git a/http/base/pom.xml b/http/base/pom.xml
new file mode 100644
index 0000000..7e108d6
--- /dev/null
+++ b/http/base/pom.xml
@@ -0,0 +1,53 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Base</name>
+    <artifactId>org.apache.felix.http.base</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.api</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/AbstractActivator.java b/http/base/src/main/java/org/apache/felix/http/base/internal/AbstractActivator.java
new file mode 100644
index 0000000..503825a
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/AbstractActivator.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.apache.felix.http.base.internal.util.SystemLogger;
+
+public abstract class AbstractActivator
+    implements BundleActivator
+{
+    private BundleContext context;
+    private DispatcherServlet dispatcher;
+    private HttpServiceController controller;
+
+    protected final BundleContext getBundleContext()
+    {
+        return this.context;
+    }
+
+    protected final DispatcherServlet getDispatcherServlet()
+    {
+        return this.dispatcher;
+    }
+
+    protected final HttpServiceController getHttpServiceController()
+    {
+        return this.controller;
+    }
+
+    public final void start(BundleContext context)
+        throws Exception
+    {
+        this.context = context;
+        this.controller = new HttpServiceController(this.context);
+        this.dispatcher = new DispatcherServlet(this.controller);
+
+        SystemLogger.get().open(context);
+        doStart();
+    }
+
+    public final void stop(BundleContext context)
+        throws Exception
+    {
+        doStop();
+
+        this.controller.unregister();
+        this.dispatcher.destroy();
+        SystemLogger.get().close();
+    }
+
+    protected abstract void doStart()
+        throws Exception;
+
+    protected abstract void doStop()
+        throws Exception;   
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/DispatcherServlet.java b/http/base/src/main/java/org/apache/felix/http/base/internal/DispatcherServlet.java
new file mode 100644
index 0000000..88504b5
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/DispatcherServlet.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+import javax.servlet.ServletConfig;
+import java.io.IOException;
+
+public final class DispatcherServlet
+    extends HttpServlet
+{
+    private final HttpServiceController controller;
+
+    public DispatcherServlet(HttpServiceController controller)
+    {
+        this.controller = controller;
+    }
+
+    @Override
+    public void init(ServletConfig config)
+        throws ServletException
+    {
+        super.init(config);
+        this.controller.register(getServletContext());
+    }
+
+    @Override
+    public void destroy()
+    {
+        this.controller.unregister();
+        super.destroy();
+    }
+
+    @Override
+    protected void service(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        this.controller.getDispatcher().dispatch(req, res);
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
new file mode 100644
index 0000000..823162b
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.HttpService;
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import org.apache.felix.http.base.internal.dispatch.Dispatcher;
+import org.apache.felix.http.base.internal.service.HttpServiceFactory;
+import org.apache.felix.http.api.ExtHttpService;
+import javax.servlet.ServletContext;
+import java.util.Hashtable;
+
+public final class HttpServiceController
+{
+    private final BundleContext bundleContext;
+    private final HandlerRegistry registry;
+    private final Dispatcher dispatcher;
+    private final Hashtable<String, Object> serviceProps;
+    private ServiceRegistration serviceReg;
+
+    public HttpServiceController(BundleContext bundleContext)
+    {
+        this.bundleContext = bundleContext;
+        this.registry = new HandlerRegistry();
+        this.dispatcher = new Dispatcher(this.registry);
+        this.serviceProps = new Hashtable<String, Object>();
+    }
+
+    public Dispatcher getDispatcher()
+    {
+        return this.dispatcher;
+    }
+
+    public void setProperties(Hashtable<String, Object> props)
+    {
+        this.serviceProps.clear();
+        this.serviceProps.putAll(props);
+
+        if (this.serviceReg != null) {
+            this.serviceReg.setProperties(this.serviceProps);
+        }
+    }
+
+    public void register(ServletContext servletContext)
+    {
+        HttpServiceFactory factory = new HttpServiceFactory(servletContext, this.registry);
+        String[] ifaces = new String[] { HttpService.class.getName(), ExtHttpService.class.getName() };
+        this.serviceReg = this.bundleContext.registerService(ifaces, factory, this.serviceProps);
+    }
+
+    public void unregister()
+    {
+        if (this.serviceReg == null) {
+            return;
+        }
+
+        try {
+            this.serviceReg.unregister();
+            this.registry.removeAll();
+        } finally {
+            this.serviceReg = null;
+        }
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/context/ExtServletContext.java b/http/base/src/main/java/org/apache/felix/http/base/internal/context/ExtServletContext.java
new file mode 100644
index 0000000..7ed2008
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/context/ExtServletContext.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.context;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public interface ExtServletContext
+    extends ServletContext
+{
+    public boolean handleSecurity(HttpServletRequest req, HttpServletResponse res)
+        throws IOException;
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/context/ServletContextImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/context/ServletContextImpl.java
new file mode 100644
index 0000000..51ed942
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/context/ServletContextImpl.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.context;
+
+import org.osgi.service.http.HttpContext;
+import org.osgi.framework.Bundle;
+import org.apache.felix.http.base.internal.util.MimeTypes;
+import org.apache.felix.http.base.internal.util.SystemLogger;
+
+import javax.servlet.ServletContext;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.net.URL;
+import java.io.InputStream;
+import java.io.IOException;
+
+public final class ServletContextImpl
+    implements ExtServletContext
+{
+    private final Bundle bundle;
+    private final ServletContext context;
+    private final HttpContext httpContext;
+    private final Map<String, Object> attributes;
+
+    public ServletContextImpl(Bundle bundle, ServletContext context, HttpContext httpContext)
+    {
+        this.bundle = bundle;
+        this.context = context;
+        this.httpContext = httpContext;
+        this.attributes = new ConcurrentHashMap<String, Object>();
+    }
+
+    public String getContextPath()
+    {
+        return this.context.getContextPath();
+    }
+
+    public ServletContext getContext(String uri)
+    {
+        return this.context.getContext(uri);
+    }
+
+    public int getMajorVersion()
+    {
+        return this.context.getMajorVersion();
+    }
+
+    public int getMinorVersion()
+    {
+        return this.context.getMinorVersion();
+    }
+
+    public Set getResourcePaths(String path)
+    {
+        Enumeration paths = this.bundle.getEntryPaths(normalizePath(path));
+        if ((paths == null) || !paths.hasMoreElements()) {
+            return null;
+        }
+
+        Set<String> set = new HashSet<String>();
+        while (paths.hasMoreElements()) {
+            set.add((String)paths.nextElement());
+        }
+
+        return set;
+    }
+
+    public URL getResource(String path)
+    {
+        return this.httpContext.getResource(normalizePath(path));
+    }
+
+    public InputStream getResourceAsStream(String path)
+    {
+        URL res = getResource(path);
+        if (res != null) {
+            try {
+                return res.openStream();
+            } catch (IOException e) {
+                // Do nothing
+            }
+        }
+
+        return null;
+    }
+
+    private String normalizePath(String path)
+    {
+        if (path == null) {
+            return null;
+        }
+
+        String normalizedPath = path.trim().replaceAll("/+", "/");
+        if (normalizedPath.startsWith("/") && (normalizedPath.length() > 1)) {
+            normalizedPath = normalizedPath.substring(1);
+        }
+
+        return normalizedPath;
+    }
+
+    public RequestDispatcher getRequestDispatcher(String uri)
+    {
+        return null;
+    }
+
+    public RequestDispatcher getNamedDispatcher(String name)
+    {
+        return null;
+    }
+
+    public String getInitParameter(String name)
+    {
+        return null;
+    }
+
+    public Enumeration getInitParameterNames()
+    {
+        return Collections.enumeration(Collections.emptyList());
+    }
+
+    public Object getAttribute(String name)
+    {
+        return this.attributes.get(name);
+    }
+
+    public Enumeration getAttributeNames()
+    {
+        return Collections.enumeration(this.attributes.keySet());
+    }
+
+    public void setAttribute(String name, Object value)
+    {
+        this.attributes.put(name, value);
+    }
+
+    public void removeAttribute(String name)
+    {
+        this.attributes.remove(name);
+    }
+
+    @SuppressWarnings("deprecation")
+    public Servlet getServlet(String name)
+        throws ServletException
+    {
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    public Enumeration getServlets()
+    {
+        return Collections.enumeration(Collections.emptyList());
+    }
+
+    @SuppressWarnings("deprecation")
+    public Enumeration getServletNames()
+    {
+        return Collections.enumeration(Collections.emptyList());
+    }
+
+    public void log(String message)
+    {
+        SystemLogger.get().info(message);
+    }
+
+    public void log(Exception cause, String message)
+    {
+        SystemLogger.get().error(message, cause);
+    }
+
+    public void log(String message, Throwable cause)
+    {
+        SystemLogger.get().error(message, cause);
+    }
+
+    public String getServletContextName()
+    {
+        return this.context.getServletContextName();
+    }
+
+    public String getRealPath(String name)
+    {
+        return null;
+    }
+
+    public String getServerInfo()
+    {
+        return this.context.getServerInfo();
+    }
+
+    public String getMimeType(String file)
+    {
+        String type = this.httpContext.getMimeType(file);
+        if (type != null) {
+            return type;
+        }
+
+        return MimeTypes.get().getByFile(file);
+    }
+
+    public boolean handleSecurity(HttpServletRequest req, HttpServletResponse res)
+        throws IOException
+    {
+        return this.httpContext.handleSecurity(req, res);
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/context/ServletContextManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/context/ServletContextManager.java
new file mode 100644
index 0000000..c461b0f
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/context/ServletContextManager.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.context;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+
+import javax.servlet.ServletContext;
+import java.util.Map;
+import java.util.HashMap;
+
+public final class ServletContextManager
+{
+    private final Bundle bundle;
+    private final ServletContext context;
+    private final Map<HttpContext, ExtServletContext> contextMap;
+
+    public ServletContextManager(Bundle bundle, ServletContext context)
+    {
+        this.bundle = bundle;
+        this.context = context;
+        this.contextMap = new HashMap<HttpContext, ExtServletContext>();
+    }
+
+    public ExtServletContext getServletContext(HttpContext httpContext)
+    {
+        synchronized (this.contextMap) {
+            ExtServletContext context = this.contextMap.get(httpContext);
+            if (context == null) {
+                context = addServletContext(httpContext);
+            }
+
+            return context;
+        }
+    }
+
+    private ExtServletContext addServletContext(HttpContext httpContext)
+    {
+        ExtServletContext context = new ServletContextImpl(this.bundle, this.context, httpContext);
+        this.contextMap.put(httpContext, context);
+        return context;
+    }
+}
\ No newline at end of file
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
new file mode 100644
index 0000000..a7e6ba0
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.dispatch;
+
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+public final class Dispatcher
+{
+    private final HandlerRegistry handlerRegistry;
+
+    public Dispatcher(HandlerRegistry handlerRegistry)
+    {
+        this.handlerRegistry = handlerRegistry;
+    }
+
+    public void dispatch(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        ServletPipeline servletPipeline = new ServletPipeline(this.handlerRegistry.getServlets());
+        FilterPipeline filterPipeline = new FilterPipeline(this.handlerRegistry.getFilters(), servletPipeline);
+        filterPipeline.dispatch(req, res, new NotFoundFilterChain());
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/FilterPipeline.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/FilterPipeline.java
new file mode 100644
index 0000000..880b78c
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/FilterPipeline.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.dispatch;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.ServletException;
+import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
+import java.io.IOException;
+import org.apache.felix.http.base.internal.handler.FilterHandler;
+
+public final class FilterPipeline
+{
+    private final FilterHandler[] handlers;
+    private final ServletPipeline servletPipeline;
+
+    public FilterPipeline(FilterHandler[] handlers, ServletPipeline servletPipeline)
+    {
+        this.handlers = handlers;
+        this.servletPipeline = servletPipeline;
+    }
+
+    public void dispatch(HttpServletRequest req, HttpServletResponse res, FilterChain proceedingChain)
+        throws ServletException, IOException
+    {
+        FilterChain chain = new InvocationFilterChain(this.handlers, this.servletPipeline, proceedingChain);
+
+        if (this.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 = servletPipeline.getRequestDispatcher(path);
+            return (null != dispatcher) ? dispatcher : super.getRequestDispatcher(path);
+        }        
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java
new file mode 100644
index 0000000..57fb49b
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/HttpFilterChain.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.dispatch;
+
+import javax.servlet.FilterChain;
+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;
+
+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;
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java
new file mode 100644
index 0000000..0072a91
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/InvocationFilterChain.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.dispatch;
+
+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;
+
+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)) {
+                this.proceedingChain.doFilter(req, res);
+            }
+        }
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java
new file mode 100644
index 0000000..3ee23b2
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/NotFoundFilterChain.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.dispatch;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public final class NotFoundFilterChain
+    extends HttpFilterChain
+{
+    protected void doFilter(HttpServletRequest req, HttpServletResponse res)
+        throws IOException, ServletException
+    {
+        res.sendError(HttpServletResponse.SC_NOT_FOUND);
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletPipeline.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletPipeline.java
new file mode 100644
index 0000000..053a09d
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletPipeline.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.dispatch;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.ServletException;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+
+public final class ServletPipeline
+{
+    private final ServletHandler[] handlers;
+
+    public ServletPipeline(ServletHandler[] handlers)
+    {
+        this.handlers = handlers;
+    }
+
+    public boolean handle(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        for (ServletHandler handler : this.handlers) {
+            if (handler.handle(req, res)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public boolean hasServletsMapped()
+    {
+        return this.handlers.length > 0;
+    }
+
+    public RequestDispatcher getRequestDispatcher(String path)
+    {
+        for (ServletHandler handler : this.handlers) {
+            if (handler.matches(path)) {
+                return new Dispatcher(path, handler);
+            }
+        }
+        
+        return null;
+    }
+
+    private final class Dispatcher
+        implements RequestDispatcher
+    {
+        private final String path;
+        private final ServletHandler handler;
+
+        public Dispatcher(String path, ServletHandler handler)
+        {
+            this.path = path;
+            this.handler = handler;
+        }
+
+        public void forward(ServletRequest req, ServletResponse res)
+            throws ServletException, IOException
+        {
+            if (res.isCommitted()) {
+                throw new ServletException("Response has been committed");
+            }
+
+            this.handler.handle(new RequestWrapper((HttpServletRequest)req, this.path), (HttpServletResponse)res);
+        }
+
+        public void include(ServletRequest req, ServletResponse res)
+            throws ServletException, IOException
+        {
+            this.handler.handle((HttpServletRequest)req, (HttpServletResponse)res);
+        }
+    }
+
+    private final class RequestWrapper
+        extends HttpServletRequestWrapper
+    {
+        private final String requestUri;
+        
+        public RequestWrapper(HttpServletRequest req, String requestUri)
+        {
+            super(req);
+            this.requestUri = requestUri;
+        }
+
+        public String getRequestURI()
+        {
+            return this.requestUri;
+        }
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java
new file mode 100644
index 0000000..f5c7041
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/AbstractHandler.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import javax.servlet.ServletException;
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class AbstractHandler
+{
+    private final static AtomicInteger ID =
+        new AtomicInteger();
+
+    private final String id;
+    private final ExtServletContext context;
+    private final Map<String, String> initParams;
+
+    public AbstractHandler(ExtServletContext context)
+    {
+        this.id = "" + ID.incrementAndGet();
+        this.context = context;
+        this.initParams = new HashMap<String, String>();
+    }
+
+    public final String getId()
+    {
+        return this.id;
+    }
+
+    protected final ExtServletContext getContext()
+    {
+        return this.context;
+    }
+
+    public final Map<String, String> getInitParams()
+    {
+        return this.initParams;
+    }
+
+    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);
+            }
+        }
+    }
+
+    public abstract void init()
+        throws ServletException;
+
+    public abstract void destroy();
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterConfigImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterConfigImpl.java
new file mode 100644
index 0000000..b807fe1
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterConfigImpl.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import java.util.Enumeration;
+import java.util.Collections;
+import java.util.Map;
+
+public final class FilterConfigImpl
+    implements FilterConfig
+{
+    private final String name;
+    private final ServletContext context;
+    private final Map<String, String> initParams;
+
+    public FilterConfigImpl(String name, ServletContext context, Map<String, String> initParams)
+    {
+        this.name = name;
+        this.context = context;
+        this.initParams = initParams;
+    }
+
+    public String getFilterName()
+    {
+        return this.name;
+    }
+
+    public ServletContext getServletContext()
+    {
+        return this.context;
+    }
+
+    public String getInitParameter(String name)
+    {
+        return this.initParams.get(name);
+    }
+
+    public Enumeration getInitParameterNames()
+    {
+        return Collections.enumeration(this.initParams.keySet());
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
new file mode 100644
index 0000000..7dc3f01
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import java.io.IOException;
+
+public final class FilterHandler
+    extends AbstractHandler implements Comparable<FilterHandler>
+{
+    private final Filter filter;
+    private final String pattern;
+    private final int ranking;
+
+    public FilterHandler(ExtServletContext context, Filter filter, String pattern, int ranking)
+    {
+        super(context);
+        this.filter = filter;
+        this.pattern = pattern;
+        this.ranking = ranking;
+    }
+
+    public Filter getFilter()
+    {
+        return this.filter;
+    }
+    
+    public void init()
+        throws ServletException
+    {
+        String name = "filter_" + getId();
+        FilterConfig config = new FilterConfigImpl(name, getContext(), getInitParams());
+        this.filter.init(config);
+    }
+
+    public void destroy()
+    {
+        this.filter.destroy();
+    }
+
+    public boolean matches(String uri)
+    {
+        return uri.matches(this.pattern);
+    }
+
+    public void handle(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
+        throws ServletException, IOException
+    {
+        final boolean matches = matches(req.getPathInfo());
+        if (matches) {
+            doHandle(req, res, chain);
+        } else {
+            chain.doFilter(req, res);
+        }
+    }
+
+    private void doHandle(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
+        throws ServletException, IOException
+    {
+        if (!getContext().handleSecurity(req, res)) {
+            res.sendError(HttpServletResponse.SC_FORBIDDEN);
+        } else {
+            this.filter.doFilter(req, res, chain);
+        }
+    }
+
+    public int compareTo(FilterHandler other)
+    {
+        return other.ranking - this.ranking;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
new file mode 100644
index 0000000..dbc04fb
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import org.osgi.service.http.NamespaceException;
+import javax.servlet.ServletException;
+import javax.servlet.Servlet;
+import javax.servlet.Filter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Arrays;
+
+public final class HandlerRegistry
+{
+    private final Map<Servlet, ServletHandler> servletMap;
+    private final Map<Filter, FilterHandler> filterMap;
+    private final Map<String, Servlet> aliasMap;
+    private ServletHandler[] servlets;
+    private FilterHandler[] filters;
+
+    public HandlerRegistry()
+    {
+        this.servletMap = new HashMap<Servlet, ServletHandler>();
+        this.filterMap = new HashMap<Filter, FilterHandler>();
+        this.aliasMap = new HashMap<String, Servlet>();
+        this.servlets = new ServletHandler[0];
+        this.filters = new FilterHandler[0];
+    }
+
+    public ServletHandler[] getServlets()
+    {
+        return this.servlets;
+    }
+
+    public FilterHandler[] getFilters()
+    {
+        return this.filters;
+    }
+
+    public synchronized void addServlet(ServletHandler handler)
+        throws ServletException, NamespaceException
+    {
+        if (this.servletMap.containsKey(handler.getServlet())) {
+            throw new ServletException("Servlet instance already registered");
+        }
+
+        if (this.aliasMap.containsKey(handler.getAlias())) {
+            throw new NamespaceException("Servlet with alias already registered");
+        }
+
+        handler.init();
+        this.servletMap.put(handler.getServlet(), handler);
+        this.aliasMap.put(handler.getAlias(), handler.getServlet());
+        updateServletArray();
+    }
+
+    public synchronized void addFilter(FilterHandler handler)
+        throws ServletException
+    {
+        if (this.filterMap.containsKey(handler.getFilter())) {
+            throw new ServletException("Filter instance already registered");
+        }
+
+        handler.init();
+        this.filterMap.put(handler.getFilter(), handler);
+        updateFilterArray();
+    }
+
+    public synchronized void removeServlet(Servlet servlet)
+    {
+        ServletHandler handler = this.servletMap.remove(servlet);
+        if (handler != null) {
+            updateServletArray();
+            this.aliasMap.remove(handler.getAlias());
+            handler.destroy();
+        }
+    }
+
+    public synchronized void removeFilter(Filter filter)
+    {
+        FilterHandler handler = this.filterMap.remove(filter);
+        if (handler != null) {
+            updateFilterArray();
+            handler.destroy();
+        }
+    }
+
+    public synchronized Servlet getServletByAlias(String alias)
+    {
+        return this.aliasMap.get(alias);
+    }
+
+    public synchronized void removeAll()
+    {
+        for (ServletHandler handler : this.servletMap.values()) {
+            handler.destroy();
+        }
+
+        for (FilterHandler handler : this.filterMap.values()) {
+            handler.destroy();
+        }
+
+        this.servletMap.clear();
+        this.filterMap.clear();
+        this.aliasMap.clear();
+
+        updateServletArray();
+        updateFilterArray();
+    }
+
+    private void updateServletArray()
+    {
+        ServletHandler[] tmp = this.servletMap.values().toArray(new ServletHandler[this.servletMap.size()]);
+        Arrays.sort(tmp);
+        this.servlets = tmp;
+    }
+
+    private void updateFilterArray()
+    {
+        FilterHandler[] tmp = this.filterMap.values().toArray(new FilterHandler[this.filterMap.size()]);
+        Arrays.sort(tmp);
+        this.filters = tmp;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletConfigImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletConfigImpl.java
new file mode 100644
index 0000000..43dc2b1
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletConfigImpl.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletConfig;
+import java.util.Enumeration;
+import java.util.Collections;
+import java.util.Map;
+
+public final class ServletConfigImpl
+    implements ServletConfig
+{
+    private final String name;
+    private final ServletContext context;
+    private final Map<String, String> initParams;
+
+    public ServletConfigImpl(String name, ServletContext context, Map<String, String> initParams)
+    {
+        this.name = name;
+        this.context = context;
+        this.initParams = initParams;
+    }
+
+    public String getServletName()
+    {
+        return this.name;
+    }
+
+    public ServletContext getServletContext()
+    {
+        return this.context;
+    }
+
+    public String getInitParameter(String name)
+    {
+        return this.initParams.get(name);
+    }
+
+    public Enumeration getInitParameterNames()
+    {
+        return Collections.enumeration(this.initParams.keySet());
+    }
+}
\ No newline at end of file
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
new file mode 100644
index 0000000..11c7a26
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletRequestWrapper;
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import java.io.IOException;
+
+public final class ServletHandler
+    extends AbstractHandler implements Comparable<ServletHandler>
+{
+    private final String alias;
+    private final Servlet servlet;
+
+    public ServletHandler(ExtServletContext context, Servlet servlet, String alias)
+    {
+        super(context);
+        this.alias = alias;
+        this.servlet = servlet;
+    }
+
+    public String getAlias()
+    {
+        return this.alias;
+    }
+
+    public Servlet getServlet()
+    {
+        return this.servlet;
+    }
+
+    public void init()
+        throws ServletException
+    {
+        String name = "servlet_" + getId();
+        ServletConfig config = new ServletConfigImpl(name, getContext(), getInitParams());
+        this.servlet.init(config);
+    }
+
+    public void destroy()
+    {
+        this.servlet.destroy();
+    }
+
+    public boolean matches(String uri)
+    {
+        if (this.alias.equals("/")) {
+            return uri.startsWith(this.alias);
+        } else {
+            return uri.equals(this.alias) || uri.startsWith(this.alias + "/");
+        }
+    }
+
+    public boolean handle(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        final boolean matches = matches(req.getPathInfo());
+        if (matches) {
+            doHandle(req, res);
+        }
+
+        return matches;
+    }
+
+    private void doHandle(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        if (!getContext().handleSecurity(req, res)) {
+            if (!res.isCommitted()) {
+                res.sendError(HttpServletResponse.SC_FORBIDDEN);
+            }
+        } else {
+            this.servlet.service(new RequestWrapper(req), res);
+        }
+    }
+
+    public int compareTo(ServletHandler other)
+    {
+        return other.alias.length() - this.alias.length();
+    }    
+
+    private final class RequestWrapper
+        extends HttpServletRequestWrapper
+    {
+        private String pathInfo;
+        private boolean pathInfoComputed = false;
+
+        public RequestWrapper(HttpServletRequest req)
+        {
+            super(req);
+        }
+
+        @Override
+        public String getPathInfo()
+        {
+            if (!this.pathInfoComputed) {
+                final int servletPathLength = getServletPath().length();
+                this.pathInfo = getRequestURI().substring(getContextPath().length()).replaceAll("[/]{2,}", "/")
+                    .substring(servletPathLength);
+
+                if ("".equals(this.pathInfo) && servletPathLength != 0) {
+                    this.pathInfo = null;
+                }
+
+                this.pathInfoComputed = true;
+            }
+
+            return this.pathInfo;
+        }
+
+        @Override
+        public String getPathTranslated()
+        {
+            final String info = getPathInfo();
+            return (null == info) ? null : getRealPath(info);
+        }
+
+        @Override
+        public String getServletPath()
+        {
+            return alias;
+        }
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/DefaultHttpContext.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/DefaultHttpContext.java
new file mode 100644
index 0000000..22a048d
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/DefaultHttpContext.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.service;
+
+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 bundle;
+
+    public DefaultHttpContext(Bundle bundle)
+    {
+        this.bundle = bundle;
+    }
+
+    public String getMimeType(String name)
+    {
+        return null;
+    }
+
+    public URL getResource(String name)
+    {
+        if (name.startsWith("/")) {
+            name = name.substring(1);
+        }
+
+        return this.bundle.getResource(name);
+    }
+
+    public boolean handleSecurity(HttpServletRequest req, HttpServletResponse res)
+    {
+        return true;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java
new file mode 100644
index 0000000..45961d5
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.service;
+
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceRegistration;
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+
+import javax.servlet.ServletContext;
+
+public final class HttpServiceFactory
+    implements ServiceFactory
+{
+    private final ServletContext context;
+    private final HandlerRegistry handlerRegistry;
+
+    public HttpServiceFactory(ServletContext context, HandlerRegistry handlerRegistry)
+    {
+        this.context = context;
+        this.handlerRegistry = handlerRegistry;
+    }
+
+    public Object getService(Bundle bundle, ServiceRegistration reg)
+    {
+        return new HttpServiceImpl(this.context, this.handlerRegistry, bundle);
+    }
+
+    public void ungetService(Bundle bundle, ServiceRegistration reg, Object service)
+    {
+        ((HttpServiceImpl)service).unregisterAll();
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
new file mode 100644
index 0000000..e972967
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.service;
+
+import org.apache.felix.http.api.ExtHttpService;
+import org.apache.felix.http.base.internal.context.ServletContextManager;
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import org.apache.felix.http.base.internal.handler.FilterHandler;
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.util.SystemLogger;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.NamespaceException;
+import org.osgi.framework.Bundle;
+import javax.servlet.Filter;
+import javax.servlet.ServletException;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import java.util.Dictionary;
+import java.util.HashSet;
+
+public final class HttpServiceImpl
+    implements ExtHttpService
+{
+    private final Bundle bundle;
+    private final HandlerRegistry handlerRegistry;
+    private final HashSet<Servlet> localServlets;
+    private final HashSet<Filter> localFilters;
+    private final ServletContextManager contextManager;
+
+    public HttpServiceImpl(ServletContext context, HandlerRegistry handlerRegistry, Bundle bundle)
+    {
+        this.bundle = bundle;
+        this.handlerRegistry = handlerRegistry;
+        this.localServlets = new HashSet<Servlet>();
+        this.localFilters = new HashSet<Filter>();
+        this.contextManager = new ServletContextManager(bundle, context);
+    }
+
+    private ExtServletContext getServletContext(HttpContext context)
+    {
+        if (context == null) {
+            context = createDefaultHttpContext();
+        }
+
+        return this.contextManager.getServletContext(context);
+    }
+
+    public void registerFilter(Filter filter, String pattern, Dictionary initParams, int ranking, HttpContext context)
+        throws ServletException
+    {
+        FilterHandler handler = new FilterHandler(getServletContext(context), filter, pattern, ranking);
+        handler.setInitParams(initParams);
+        this.handlerRegistry.addFilter(handler);
+        this.localFilters.add(filter);
+    }
+
+    public void unregisterFilter(Filter filter)
+    {
+        if (filter != null) {
+            this.handlerRegistry.removeFilter(filter);
+            this.localFilters.remove(filter);
+        }
+    }
+
+    public void unregisterServlet(Servlet servlet)
+    {
+        if (servlet != null) {
+            this.handlerRegistry.removeServlet(servlet);
+            this.localServlets.remove(servlet);
+        }
+    }
+
+    public void registerServlet(String alias, Servlet servlet, Dictionary initParams, HttpContext context)
+        throws ServletException, NamespaceException
+    {
+        if (!isAliasValid(alias)) {
+            throw new IllegalArgumentException( "Malformed servlet alias [" + alias + "]");
+        }
+        
+        ServletHandler handler = new ServletHandler(getServletContext(context), servlet, alias);
+        handler.setInitParams(initParams);
+        this.handlerRegistry.addServlet(handler);
+        this.localServlets.add(servlet);
+    }
+
+    public void registerResources(String alias, String name, HttpContext context)
+        throws NamespaceException
+    {
+        if (!isNameValid(name)) {
+            throw new IllegalArgumentException( "Malformed resource name [" + name + "]");
+        }
+        
+        try {
+            Servlet servlet = new ResourceServlet(name);
+            registerServlet(alias, servlet, null, context);
+        } catch (ServletException e) {
+            SystemLogger.get().error("Failed to register resources", e);
+        }
+    }
+
+    public void unregister(String alias)
+    {
+        unregisterServlet(this.handlerRegistry.getServletByAlias(alias));
+    }
+
+    public HttpContext createDefaultHttpContext()
+    {
+        return new DefaultHttpContext(this.bundle);
+    }
+
+    public void unregisterAll()
+    {
+        HashSet<Servlet> servlets = new HashSet<Servlet>(this.localServlets);
+        for (Servlet servlet : servlets) {
+            unregisterServlet(servlet);
+        }
+
+        HashSet<Filter> filters = new HashSet<Filter>(this.localFilters);
+        for (Filter fiter : filters) {
+            unregisterFilter(fiter);
+        }
+    }
+
+    private boolean isNameValid(String name)
+    {
+        if (name == null) {
+            return false;
+        }
+
+        if (name.endsWith( "/" )) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean isAliasValid(String alias)
+    {
+        if (alias == null) {
+            return false;
+        }
+
+        if (!alias.equals("/") && ( !alias.startsWith("/") || alias.endsWith("/"))) {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/ResourceServlet.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/ResourceServlet.java
new file mode 100644
index 0000000..7479930
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/ResourceServlet.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.service;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+public final class ResourceServlet 
+    extends HttpServlet
+{
+    private final String path;
+
+    public ResourceServlet(String path)
+    {
+        this.path = path;
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        String target = req.getPathInfo();
+        if (target == null) {
+            target = "";
+        }
+
+        if (!target.startsWith("/")) {
+            target += "/" + target;
+        }
+
+        String resName = this.path + target;
+        URL url = getServletContext().getResource(resName);
+        
+        if (url == null) {
+            res.sendError(HttpServletResponse.SC_NOT_FOUND);
+        } else {
+            handle(req, res, url, resName);
+        }
+    }
+
+    private void handle(HttpServletRequest req, HttpServletResponse res, URL url, String resName)
+        throws IOException
+    {
+        String contentType = getServletContext().getMimeType(resName);
+        if (contentType != null) {
+            res.setContentType(contentType);
+        }
+
+        long lastModified  = getLastModified(url);
+        if (lastModified != 0) {
+            res.setDateHeader("Last-Modified", lastModified);
+        }
+
+        if (!resourceModified(lastModified, req.getDateHeader("If-Modified-Since"))) {
+            res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+        } else {
+            copyResource(url, res);
+        }
+    }
+
+    private long getLastModified(URL url)
+    {
+        long lastModified = 0;
+
+        try {
+            URLConnection conn = url.openConnection();
+            lastModified = conn.getLastModified();
+        } catch (Exception e)
+        {
+            // Do nothing
+        }
+
+        if (lastModified == 0) {
+            String filepath = url.getPath();
+            if (filepath != null) {
+                File f = new File(filepath);
+                if (f.exists()) {
+                    lastModified = f.lastModified();
+                }
+            }
+        }
+
+        return lastModified;
+    }
+
+    private boolean resourceModified(long resTimestamp, long modSince)
+    {
+        modSince /= 1000;
+        resTimestamp /= 1000;
+
+        return resTimestamp == 0 || modSince == -1 || resTimestamp > modSince;
+    }
+
+    private void copyResource(URL url, HttpServletResponse res)
+        throws IOException
+    {
+        OutputStream os = null;
+        InputStream is = null;
+
+        try {
+            os = res.getOutputStream();
+            is = url.openStream();
+
+            int len = 0;
+            byte[] buf = new byte[1024];
+            int n;
+
+            while ((n = is.read(buf, 0, buf.length)) >= 0) {
+                os.write( buf, 0, n );
+                len += n;
+            }
+
+            res.setContentLength(len);
+        } finally {
+            if (is != null) {
+                is.close();
+            }
+
+            if (os != null) {
+                os.close();
+            }
+        }
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/util/MimeTypes.java b/http/base/src/main/java/org/apache/felix/http/base/internal/util/MimeTypes.java
new file mode 100644
index 0000000..3833fda
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/util/MimeTypes.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.util;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public final class MimeTypes
+{
+    private final static MimeTypes INSTANCE =
+            new MimeTypes();
+
+    private final Map<String, String> extMap;
+
+    private MimeTypes()
+    {
+        this.extMap = new HashMap<String, String>();
+        this.extMap.put("abs", "audio/x-mpeg");
+        this.extMap.put("ai", "application/postscript");
+        this.extMap.put("aif", "audio/x-aiff");
+        this.extMap.put("aifc", "audio/x-aiff");
+        this.extMap.put("aiff", "audio/x-aiff");
+        this.extMap.put("aim", "application/x-aim");
+        this.extMap.put("art", "image/x-jg");
+        this.extMap.put("asf", "video/x-ms-asf");
+        this.extMap.put("asx", "video/x-ms-asf");
+        this.extMap.put("au", "audio/basic");
+        this.extMap.put("avi", "video/x-msvideo");
+        this.extMap.put("avx", "video/x-rad-screenplay");
+        this.extMap.put("bcpio", "application/x-bcpio");
+        this.extMap.put("bin", "application/octet-stream");
+        this.extMap.put("bmp", "image/bmp");
+        this.extMap.put("body", "text/html");
+        this.extMap.put("cdf", "application/x-cdf");
+        this.extMap.put("cer", "application/x-x509-ca-cert");
+        this.extMap.put("class", "application/java");
+        this.extMap.put("cpio", "application/x-cpio");
+        this.extMap.put("csh", "application/x-csh");
+        this.extMap.put("css", "text/css");
+        this.extMap.put("dib", "image/bmp");
+        this.extMap.put("doc", "application/msword");
+        this.extMap.put("dtd", "application/xml-dtd");
+        this.extMap.put("dv", "video/x-dv");
+        this.extMap.put("dvi", "application/x-dvi");
+        this.extMap.put("eps", "application/postscript");
+        this.extMap.put("etx", "text/x-setext");
+        this.extMap.put("exe", "application/octet-stream");
+        this.extMap.put("gif", "image/gif");
+        this.extMap.put("gk", "application/octet-stream");
+        this.extMap.put("gtar", "application/x-gtar");
+        this.extMap.put("gz", "application/x-gzip");
+        this.extMap.put("hdf", "application/x-hdf");
+        this.extMap.put("hqx", "application/mac-binhex40");
+        this.extMap.put("htc", "text/x-component");
+        this.extMap.put("htm", "text/html");
+        this.extMap.put("html", "text/html");
+        this.extMap.put("hqx", "application/mac-binhex40");
+        this.extMap.put("ief", "image/ief");
+        this.extMap.put("jad", "text/vnd.sun.j2me.app-descriptor");
+        this.extMap.put("jar", "application/java-archive");
+        this.extMap.put("java", "text/plain");
+        this.extMap.put("jnlp", "application/x-java-jnlp-file");
+        this.extMap.put("jpe", "image/jpeg");
+        this.extMap.put("jpeg", "image/jpeg");
+        this.extMap.put("jpg", "image/jpeg");
+        this.extMap.put("js", "text/javascript");
+        this.extMap.put("kar", "audio/x-midi");
+        this.extMap.put("latex", "application/x-latex");
+        this.extMap.put("m3u", "audio/x-mpegurl");
+        this.extMap.put("mac", "image/x-macpaint");
+        this.extMap.put("man", "application/x-troff-man");
+        this.extMap.put("mathml", "application/mathml+xml");
+        this.extMap.put("me", "application/x-troff-me");
+        this.extMap.put("mid", "audio/x-midi");
+        this.extMap.put("midi", "audio/x-midi");
+        this.extMap.put("mif", "application/x-mif");
+        this.extMap.put("mov", "video/quicktime");
+        this.extMap.put("movie", "video/x-sgi-movie");
+        this.extMap.put("mp1", "audio/x-mpeg");
+        this.extMap.put("mp2", "audio/x-mpeg");
+        this.extMap.put("mp3", "audio/x-mpeg");
+        this.extMap.put("mpa", "audio/x-mpeg");
+        this.extMap.put("mpe", "video/mpeg");
+        this.extMap.put("mpeg", "video/mpeg");
+        this.extMap.put("mpega", "audio/x-mpeg");
+        this.extMap.put("mpg", "video/mpeg");
+        this.extMap.put("mpv2", "video/mpeg2");
+        this.extMap.put("ms", "application/x-wais-source");
+        this.extMap.put("nc", "application/x-netcdf");
+        this.extMap.put("oda", "application/oda");
+        this.extMap.put("ogg", "application/ogg");
+        this.extMap.put("pbm", "image/x-portable-bitmap");
+        this.extMap.put("pct", "image/pict");
+        this.extMap.put("pdf", "application/pdf");
+        this.extMap.put("pgm", "image/x-portable-graymap");
+        this.extMap.put("pic", "image/pict");
+        this.extMap.put("pict", "image/pict");
+        this.extMap.put("pls", "audio/x-scpls");
+        this.extMap.put("png", "image/png");
+        this.extMap.put("pnm", "image/x-portable-anymap");
+        this.extMap.put("pnt", "image/x-macpaint");
+        this.extMap.put("ppm", "image/x-portable-pixmap");
+        this.extMap.put("ppt", "application/powerpoint");
+        this.extMap.put("ps", "application/postscript");
+        this.extMap.put("psd", "image/x-photoshop");
+        this.extMap.put("qt", "video/quicktime");
+        this.extMap.put("qti", "image/x-quicktime");
+        this.extMap.put("qtif", "image/x-quicktime");
+        this.extMap.put("ras", "image/x-cmu-raster");
+        this.extMap.put("rdf", "application/rdf+xml");
+        this.extMap.put("rgb", "image/x-rgb");
+        this.extMap.put("rm", "application/vnd.rn-realmedia");
+        this.extMap.put("roff", "application/x-troff");
+        this.extMap.put("rtf", "application/rtf");
+        this.extMap.put("rtx", "text/richtext");
+        this.extMap.put("sh", "application/x-sh");
+        this.extMap.put("shar", "application/x-shar");
+        this.extMap.put("shtml", "text/x-server-parsed-html");
+        this.extMap.put("sit", "application/x-stuffit");
+        this.extMap.put("smf", "audio/x-midi");
+        this.extMap.put("snd", "audio/basic");
+        this.extMap.put("src", "application/x-wais-source");
+        this.extMap.put("sv4cpio", "application/x-sv4cpio");
+        this.extMap.put("sv4crc", "application/x-sv4crc");
+        this.extMap.put("svg", "image/svg+xml");
+        this.extMap.put("svgz", "image/svg+xml");
+        this.extMap.put("swf", "application/x-shockwave-flash");
+        this.extMap.put("t", "application/x-troff");
+        this.extMap.put("tar", "application/x-tar");
+        this.extMap.put("tcl", "application/x-tcl");
+        this.extMap.put("tex", "application/x-tex");
+        this.extMap.put("texi", "application/x-texinfo");
+        this.extMap.put("texinfo", "application/x-texinfo");
+        this.extMap.put("tif", "image/tiff");
+        this.extMap.put("tiff", "image/tiff");
+        this.extMap.put("tr", "application/x-troff");
+        this.extMap.put("tsv", "text/tab-separated-values");
+        this.extMap.put("txt", "text/plain");
+        this.extMap.put("ulw", "audio/basic");
+        this.extMap.put("ustar", "application/x-ustar");
+        this.extMap.put("xbm", "image/x-xbitmap");
+        this.extMap.put("xml", "text/xml");
+        this.extMap.put("xpm", "image/x-xpixmap");
+        this.extMap.put("xsl", "application/xml");
+        this.extMap.put("xslt", "application/xslt+xml");
+        this.extMap.put("xwd", "image/x-xwindowdump");
+        this.extMap.put("vsd", "application/x-visio");
+        this.extMap.put("vxml", "application/voicexml+xml");
+        this.extMap.put("wav", "audio/x-wav");
+        this.extMap.put("wbmp", "image/vnd.wap.wbmp");
+        this.extMap.put("wml", "text/vnd.wap.wml");
+        this.extMap.put("wmlc", "application/vnd.wap.wmlc");
+        this.extMap.put("wmls", "text/vnd.wap.wmls");
+        this.extMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
+        this.extMap.put("wrl", "x-world/x-vrml");
+        this.extMap.put("xht", "application/xhtml+xml");
+        this.extMap.put("xhtml", "application/xhtml+xml");
+        this.extMap.put("xls", "application/vnd.ms-excel");
+        this.extMap.put("xul", "application/vnd.mozilla.xul+xml");
+        this.extMap.put("Z", "application/x-compress");
+        this.extMap.put("z", "application/x-compress");
+        this.extMap.put("zip", "application/zip");
+    }
+
+    public String getByFile(String file)
+    {
+        if (file == null) {
+            return null;
+        }
+
+        int dot = file.lastIndexOf(".");
+        if (dot < 0) {
+            return null;
+        }
+
+        String ext = file.substring(dot + 1);
+        if (ext.length() < 1) {
+            return null;
+        }
+
+        return getByExtension(ext);
+    }
+
+    public String getByExtension(String ext)
+    {
+        if (ext == null) {
+            return null;
+        }
+
+        return this.extMap.get(ext);
+    }
+
+    public static MimeTypes get()
+    {
+        return INSTANCE;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/util/SystemLogger.java b/http/base/src/main/java/org/apache/felix/http/base/internal/util/SystemLogger.java
new file mode 100644
index 0000000..484c027
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/util/SystemLogger.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.util;
+
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public final class SystemLogger
+{
+    private final static SystemLogger INSTANCE =
+        new SystemLogger();
+
+    private ServiceTracker tracker;
+
+    private SystemLogger()
+    {
+    }
+
+    public void open(BundleContext context)
+    {
+        if (this.tracker != null) {
+            return;
+        }
+        
+        this.tracker = new ServiceTracker(context, LogService.class.getName(), null);
+        this.tracker.open();
+    }
+
+    public void close()
+    {
+        this.tracker.close();
+        this.tracker = null;
+    }
+
+    public void debug(String message)
+    {
+        log(LogService.LOG_DEBUG, message, null);
+    }
+
+    public void info(String message)
+    {
+        log(LogService.LOG_INFO, message, null);
+    }
+
+    public void warning(String message, Throwable cause)
+    {
+        log(LogService.LOG_WARNING, message, cause);
+    }
+
+    public void error(String message, Throwable cause)
+    {
+        log(LogService.LOG_ERROR, message, cause);
+    }
+
+    private void log(int level, String message, Throwable cause)
+    {
+        LogService log = (LogService)this.tracker.getService();
+        if (log != null) {
+            log.log(level, message, cause);
+        } else {
+            System.out.println(message);
+            if (cause != null) {
+                cause.printStackTrace(System.out);
+            }
+        }
+    }
+
+    public static SystemLogger get()
+    {
+        return INSTANCE;
+    }
+}
\ No newline at end of file
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/context/ServletContextImplTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/context/ServletContextImplTest.java
new file mode 100644
index 0000000..0026a0e
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/context/ServletContextImplTest.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.context;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Assert;
+import org.mockito.Mockito;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletContext;
+import java.net.URL;
+import java.util.*;
+
+public class ServletContextImplTest
+{
+    private Bundle bundle;
+    private HttpContext httpContext;
+    private ServletContextImpl context;
+
+    @Before
+    public void setUp()
+    {
+        this.bundle = Mockito.mock(Bundle.class);
+        ServletContext globalContext = Mockito.mock(ServletContext.class);
+        this.httpContext = Mockito.mock(HttpContext.class);
+        this.context = new ServletContextImpl(this.bundle, globalContext, this.httpContext);
+    }
+
+    @Test
+    public void testGetResource()
+        throws Exception
+    {
+        URL url = getClass().getResource("resource.txt");
+        Assert.assertNotNull(url);
+        
+        Mockito.when(this.httpContext.getResource("resource.txt")).thenReturn(url);
+        Assert.assertNull(this.context.getResource("/notfound.txt"));
+        Assert.assertEquals(url, this.context.getResource("/resource.txt"));
+    }
+
+    @Test
+    public void testGetResourceAsStream()
+        throws Exception
+    {
+        URL url = getClass().getResource("resource.txt");
+        Assert.assertNotNull(url);
+
+        Mockito.when(this.httpContext.getResource("resource.txt")).thenReturn(url);
+        Assert.assertNull(this.context.getResourceAsStream("/notfound.txt"));
+        Assert.assertNotNull(this.context.getResourceAsStream("/resource.txt"));
+    }
+
+    @Test
+    public void testGetResourcePaths()
+    {
+        HashSet<String> paths = new HashSet<String>(Arrays.asList("/some/path/1", "/some/path/2"));
+        Mockito.when(this.bundle.getEntryPaths("some/path")).thenReturn(Collections.enumeration(paths));
+
+        Set set = this.context.getResourcePaths("/some/path");
+        Assert.assertNotNull(set);
+        Assert.assertEquals(2, set.size());
+        Assert.assertTrue(set.contains("/some/path/1"));
+        Assert.assertTrue(set.contains("/some/path/2"));
+    }
+
+    @Test
+    public void testGetRealPath()
+    {
+        Assert.assertNull(this.context.getRealPath("path"));
+    }
+
+    @Test
+    public void testGetInitParameter()
+    {
+        Assert.assertNull(this.context.getInitParameter("key1"));
+    }
+
+    @Test
+    public void testGetInitParameterNames()
+    {
+        Enumeration e = this.context.getInitParameterNames();
+        Assert.assertNotNull(e);
+        Assert.assertFalse(e.hasMoreElements());
+    }
+
+    @Test
+    public void testGetAttribute()
+    {
+        Assert.assertNull(this.context.getAttribute("key1"));
+
+        this.context.setAttribute("key1", "value1");
+        Assert.assertEquals("value1", this.context.getAttribute("key1"));
+
+        this.context.removeAttribute("key1");
+        Assert.assertNull(this.context.getAttribute("key1"));
+    }
+
+    @Test
+    public void testGetAttributeNames()
+    {
+        Enumeration e = this.context.getAttributeNames();
+        Assert.assertNotNull(e);
+        Assert.assertFalse(e.hasMoreElements());
+
+        this.context.setAttribute("key1", "value1");
+        e = this.context.getAttributeNames();
+        Assert.assertNotNull(e);
+        Assert.assertTrue(e.hasMoreElements());
+        Assert.assertEquals("key1", e.nextElement());
+        Assert.assertFalse(e.hasMoreElements());
+    }
+
+    @Test
+    public void testGetServlet()
+        throws Exception
+    {
+        Assert.assertNull(this.context.getServlet("test"));
+    }
+
+    @Test
+    public void testGetServletNames()
+    {
+        Enumeration e = this.context.getServletNames();
+        Assert.assertNotNull(e);
+        Assert.assertFalse(e.hasMoreElements());
+    }
+
+    @Test
+    public void testGetServlets()
+    {
+        Enumeration e = this.context.getServlets();
+        Assert.assertNotNull(e);
+        Assert.assertFalse(e.hasMoreElements());
+    }
+
+    @Test
+    public void testGetMimeType()
+    {
+        Mockito.when(this.httpContext.getMimeType("file.xml")).thenReturn("some-other-format");
+        Assert.assertEquals("some-other-format", this.context.getMimeType("file.xml"));
+        Assert.assertEquals("text/plain", this.context.getMimeType("file.txt"));
+    }
+
+    @Test
+    public void testHandleSecurity()
+        throws Exception
+    {
+        HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse res = Mockito.mock(HttpServletResponse.class);
+
+        Mockito.when(this.httpContext.handleSecurity(req, res)).thenReturn(true);
+        Assert.assertTrue(this.context.handleSecurity(req, res));
+
+        Mockito.when(this.httpContext.handleSecurity(req, res)).thenReturn(false);
+        Assert.assertFalse(this.context.handleSecurity(req, res));
+    }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/context/ServletContextManagerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/context/ServletContextManagerTest.java
new file mode 100644
index 0000000..5fbaa2e
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/context/ServletContextManagerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.context;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Assert;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+import org.mockito.Mockito;
+
+import javax.servlet.ServletContext;
+
+public class ServletContextManagerTest
+{
+    private ServletContextManager manager;
+
+    @Before
+    public void setUp()
+    {
+        Bundle bundle = Mockito.mock(Bundle.class);
+        ServletContext globalContext = Mockito.mock(ServletContext.class);
+        this.manager = new ServletContextManager(bundle, globalContext);
+    }
+
+    @Test
+    public void testGetServletContext()
+    {
+        HttpContext httpCtx = Mockito.mock(HttpContext.class);
+        ServletContext result1 = this.manager.getServletContext(httpCtx);
+        ServletContext result2 = this.manager.getServletContext(httpCtx);
+
+        Assert.assertNotNull(result1);
+        Assert.assertNotNull(result2);
+        Assert.assertSame(result1, result2);
+
+        httpCtx = Mockito.mock(HttpContext.class);
+        result2 = this.manager.getServletContext(httpCtx);
+
+        Assert.assertNotNull(result2);
+        Assert.assertNotSame(result1, result2);
+    }
+    
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/AbstractHandlerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/AbstractHandlerTest.java
new file mode 100644
index 0000000..9651998
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/AbstractHandlerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import org.junit.Test;
+import org.junit.Assert;
+import org.mockito.Mockito;
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import java.util.Hashtable;
+
+public abstract class AbstractHandlerTest
+{
+    protected ExtServletContext context;
+
+    protected abstract AbstractHandler createHandler();
+
+    public void setUp()
+    {
+        this.context = Mockito.mock(ExtServletContext.class);
+    }
+    
+    @Test
+    public void testId()
+    {
+        AbstractHandler h1 = createHandler();
+        AbstractHandler h2 = createHandler();
+
+        Assert.assertNotNull(h1.getId());
+        Assert.assertNotNull(h2.getId());
+        Assert.assertFalse(h1.getId().equals(h2.getId()));
+    }
+
+    @Test
+    public void testInitParams()
+    {
+        AbstractHandler handler = createHandler();
+        Assert.assertEquals(0, handler.getInitParams().size());
+        
+        Hashtable<String, String> map = new Hashtable<String, String>();
+        map.put("key1", "value1");
+
+        handler.setInitParams(map);
+        Assert.assertEquals(1, handler.getInitParams().size());
+        Assert.assertEquals("value1", handler.getInitParams().get("key1"));
+    }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterConfigImplTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterConfigImplTest.java
new file mode 100644
index 0000000..f5cc655
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterConfigImplTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import org.junit.Before;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import javax.servlet.ServletContext;
+import java.util.HashMap;
+import java.util.Enumeration;
+
+public class FilterConfigImplTest
+{
+    private ServletContext context;
+    private FilterConfigImpl config;
+
+    @Before
+    public void setUp()
+    {
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("key1", "value1");
+        
+        this.context = Mockito.mock(ServletContext.class);
+        this.config = new FilterConfigImpl("myfilter", this.context, params);
+    }
+
+    @Test
+    public void testGetFilterName()
+    {
+        Assert.assertSame("myfilter", this.config.getFilterName());
+    }
+
+    @Test
+    public void testGetServletContext()
+    {
+        Assert.assertSame(this.context, this.config.getServletContext());
+    }
+
+    @Test
+    public void testGetInitParameter()
+    {
+        Assert.assertNull(this.config.getInitParameter("key2"));
+        Assert.assertEquals("value1", this.config.getInitParameter("key1"));
+    }
+
+    @Test
+    public void testGetInitParameterNames()
+    {
+        Enumeration e = this.config.getInitParameterNames();
+        Assert.assertNotNull(e);
+        Assert.assertTrue(e.hasMoreElements());
+        Assert.assertEquals("key1", e.nextElement());
+        Assert.assertFalse(e.hasMoreElements());
+    }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
new file mode 100644
index 0000000..dcce161
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.Assert;
+import org.mockito.Mockito;
+import org.mockito.MockSettings;
+import org.mockito.stubbing.Answer;
+import org.hamcrest.Matcher;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class FilterHandlerTest
+    extends AbstractHandlerTest
+{
+    private Filter filter;
+
+    @Before
+    public void setUp()
+    {
+        super.setUp();
+        this.filter = Mockito.mock(Filter.class);
+    }
+
+    protected AbstractHandler createHandler()
+    {
+        return createHandler("dummy", 0);
+    }
+
+    private FilterHandler createHandler(String pattern, int ranking)
+    {
+        return new FilterHandler(this.context, this.filter, pattern, ranking);
+    }
+
+    @Test
+    public void testCompare()
+    {
+        FilterHandler h1 = createHandler("a", 0);
+        FilterHandler h2 = createHandler("b", 10);
+
+        Assert.assertEquals(10, h1.compareTo(h2));
+        Assert.assertEquals(-10, h2.compareTo(h1));
+    }
+
+    @Test
+    public void testMatches()
+    {
+        FilterHandler h1 = createHandler("/a/b", 0);
+        FilterHandler h2 = createHandler("/a/b/.+", 0);
+
+        Assert.assertTrue(h1.matches("/a/b"));
+        Assert.assertFalse(h1.matches("/a/b/c"));
+        Assert.assertTrue(h2.matches("/a/b/c"));
+        Assert.assertFalse(h2.matches("/a/b/"));
+    }
+
+    @Test
+    public void testInit()
+        throws Exception
+    {
+        FilterHandler h1 = createHandler("/a", 0);
+        h1.init();
+        Mockito.verify(this.filter).init(Mockito.any(FilterConfig.class));
+    }
+
+    @Test
+    public void testDestroy()
+    {
+        FilterHandler h1 = createHandler("/a", 0);
+        h1.destroy();
+        Mockito.verify(this.filter).destroy();
+    }
+
+    @Test
+    public void testHandleNotFound()
+        throws Exception
+    {
+        FilterHandler h1 = createHandler("/a", 0);
+        HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse res = Mockito.mock(HttpServletResponse.class);
+        FilterChain chain = Mockito.mock(FilterChain.class);
+
+        Mockito.when(req.getPathInfo()).thenReturn("/");
+        h1.handle(req, res, chain);
+
+        Mockito.verify(this.filter, Mockito.never()).doFilter(req, res, chain);
+        Mockito.verify(chain).doFilter(req, res);
+    }
+
+    @Test
+    public void testHandleFound()
+        throws Exception
+    {
+        FilterHandler h1 = createHandler("/a", 0);
+        HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse res = Mockito.mock(HttpServletResponse.class);
+        FilterChain chain = Mockito.mock(FilterChain.class);
+        Mockito.when(this.context.handleSecurity(req, res)).thenReturn(true);
+
+        Mockito.when(req.getPathInfo()).thenReturn("/a");
+        h1.handle(req, res, chain);
+
+        Mockito.verify(this.filter).doFilter(req, res, chain);
+        Mockito.verify(chain, Mockito.never()).doFilter(req, res);
+    }
+
+    @Test
+    public void testHandleFoundForbidden()
+        throws Exception
+    {
+        FilterHandler h1 = createHandler("/a", 0);
+        HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse res = Mockito.mock(HttpServletResponse.class);
+        FilterChain chain = Mockito.mock(FilterChain.class);
+        Mockito.when(this.context.handleSecurity(req, res)).thenReturn(false);
+
+        Mockito.when(req.getPathInfo()).thenReturn("/a");
+        h1.handle(req, res, chain);
+
+        Mockito.verify(this.filter, Mockito.never()).doFilter(req, res, chain);
+        Mockito.verify(chain, Mockito.never()).doFilter(req, res);
+        Mockito.verify(res).sendError(HttpServletResponse.SC_FORBIDDEN);
+    }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletConfigImplTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletConfigImplTest.java
new file mode 100644
index 0000000..f245c74
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletConfigImplTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import org.junit.Before;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import javax.servlet.ServletContext;
+import java.util.HashMap;
+import java.util.Enumeration;
+
+public class ServletConfigImplTest
+{
+    private ServletContext context;
+    private ServletConfigImpl config;
+
+    @Before
+    public void setUp()
+    {
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("key1", "value1");
+
+        this.context = Mockito.mock(ServletContext.class);
+        this.config = new ServletConfigImpl("myservlet", this.context, params);
+    }
+
+    @Test
+    public void testGetServletName()
+    {
+        Assert.assertSame("myservlet", this.config.getServletName());
+    }
+
+    @Test
+    public void testGetServletContext()
+    {
+        Assert.assertSame(this.context, this.config.getServletContext());
+    }
+
+    @Test
+    public void testGetInitParameter()
+    {
+        Assert.assertNull(this.config.getInitParameter("key2"));
+        Assert.assertEquals("value1", this.config.getInitParameter("key1"));
+    }
+
+    @Test
+    public void testGetInitParameterNames()
+    {
+        Enumeration e = this.config.getInitParameterNames();
+        Assert.assertNotNull(e);
+        Assert.assertTrue(e.hasMoreElements());
+        Assert.assertEquals("key1", e.nextElement());
+        Assert.assertFalse(e.hasMoreElements());
+    }
+}
\ No newline at end of file
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java
new file mode 100644
index 0000000..d0a44a4
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Assert;
+import org.mockito.Mockito;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class ServletHandlerTest
+    extends AbstractHandlerTest
+{
+    private Servlet servlet;
+
+    @Before
+    public void setUp()
+    {
+        super.setUp();
+        this.servlet = Mockito.mock(Servlet.class);
+    }
+
+    protected AbstractHandler createHandler()
+    {
+        return createHandler("/dummy");
+    }
+    
+    private ServletHandler createHandler(String alias)
+    {
+        return new ServletHandler(this.context, this.servlet, alias);
+    }
+    
+    @Test
+    public void testCompare()
+    {
+        ServletHandler h1 = createHandler("/a");
+        ServletHandler h2 = createHandler("/a/b");
+
+        Assert.assertEquals(2, h1.compareTo(h2));
+        Assert.assertEquals(-2, h2.compareTo(h1));
+    }
+
+    @Test
+    public void testMatches()
+    {
+        ServletHandler h1 = createHandler("/a/b");
+
+        Assert.assertFalse(h1.matches("/a/"));
+        Assert.assertTrue(h1.matches("/a/b"));
+        Assert.assertTrue(h1.matches("/a/b/"));
+        Assert.assertTrue(h1.matches("/a/b/c"));
+    }
+
+    @Test
+    public void testInit()
+        throws Exception
+    {
+        ServletHandler h1 = createHandler("/a");
+        h1.init();
+        Mockito.verify(this.servlet).init(Mockito.any(ServletConfig.class));
+    }
+
+    @Test
+    public void testDestroy()
+    {
+        ServletHandler h1 = createHandler("/a");
+        h1.destroy();
+        Mockito.verify(this.servlet).destroy();
+    }
+
+    @Test
+    public void testHandleNotFound()
+        throws Exception
+    {
+        ServletHandler h1 = createHandler("/a");
+        HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse res = Mockito.mock(HttpServletResponse.class);
+
+        Mockito.when(req.getPathInfo()).thenReturn("/");
+        boolean result = h1.handle(req, res);
+
+        Assert.assertFalse(result);
+        Mockito.verify(this.servlet, Mockito.never()).service(req, res);
+    }
+
+    @Test
+    public void testHandleFound()
+        throws Exception
+    {
+        ServletHandler h1 = createHandler("/a");
+        HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse res = Mockito.mock(HttpServletResponse.class);
+        Mockito.when(this.context.handleSecurity(req, res)).thenReturn(true);
+
+        Mockito.when(req.getPathInfo()).thenReturn("/a/b");
+        boolean result = h1.handle(req, res);
+
+        Assert.assertTrue(result);
+        Mockito.verify(this.servlet).service(Mockito.any(HttpServletRequest.class),
+                Mockito.any(HttpServletResponse.class));
+    }
+
+    @Test
+    public void testHandleFoundForbidden()
+        throws Exception
+    {
+        ServletHandler h1 = createHandler("/a");
+        HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
+        HttpServletResponse res = Mockito.mock(HttpServletResponse.class);
+        Mockito.when(this.context.handleSecurity(req, res)).thenReturn(false);
+
+        Mockito.when(req.getPathInfo()).thenReturn("/a/b");
+        boolean result = h1.handle(req, res);
+
+        Assert.assertTrue(result);
+        Mockito.verify(this.servlet, Mockito.never()).service(req, res);
+        Mockito.verify(res).sendError(HttpServletResponse.SC_FORBIDDEN);
+    }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/util/MimeTypesTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/util/MimeTypesTest.java
new file mode 100644
index 0000000..df0fcdb
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/util/MimeTypesTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.util;
+
+import org.junit.Test;
+import org.junit.Assert;
+
+public class MimeTypesTest
+{
+    @Test
+    public void testSingleton()
+    {
+        MimeTypes m1 = MimeTypes.get();
+        MimeTypes m2 = MimeTypes.get();
+
+        Assert.assertNotNull(m1);
+        Assert.assertSame(m1, m2);
+
+    }
+
+    @Test
+    public void testGetByFile()
+    {
+        Assert.assertNull(MimeTypes.get().getByFile(null));
+        Assert.assertEquals("text/plain", MimeTypes.get().getByFile("afile.txt"));
+        Assert.assertEquals("text/xml", MimeTypes.get().getByFile(".xml"));
+        Assert.assertNull(MimeTypes.get().getByFile("xml"));
+        Assert.assertNull(MimeTypes.get().getByFile("somefile.notfound"));
+    }
+
+    @Test
+    public void testGetByExtension()
+    {
+        Assert.assertNull(MimeTypes.get().getByExtension(null));
+        Assert.assertEquals("text/plain", MimeTypes.get().getByExtension("txt"));
+        Assert.assertEquals("text/xml", MimeTypes.get().getByExtension("xml"));
+        Assert.assertNull(MimeTypes.get().getByExtension("notfound"));
+    }
+}
diff --git a/http/base/src/test/resources/org/apache/felix/http/base/internal/context/resource.txt b/http/base/src/test/resources/org/apache/felix/http/base/internal/context/resource.txt
new file mode 100644
index 0000000..f99bfac
--- /dev/null
+++ b/http/base/src/test/resources/org/apache/felix/http/base/internal/context/resource.txt
@@ -0,0 +1 @@
+Dummy resource...
diff --git a/http/bridge/pom.xml b/http/bridge/pom.xml
new file mode 100644
index 0000000..c043b99
--- /dev/null
+++ b/http/bridge/pom.xml
@@ -0,0 +1,87 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Bridge</name>
+    <artifactId>org.apache.felix.http.bridge</artifactId>
+    <packaging>bundle</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>
+                            org.apache.felix.http.bridge.internal.BridgeActivator
+                        </Bundle-Activator>
+                        <Export-Package>
+                            org.apache.felix.http.api;version=${pom.version},
+                            org.osgi.service.http;version=1.2.0
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.felix.http.base.*,
+                            org.apache.felix.http.bridge.internal.*
+                        </Private-Package>
+                        <Import-Package>
+                            javax.servlet.*,
+                            *;resolution:=optional
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.api</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.base</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/bridge/src/main/java/org/apache/felix/http/bridge/internal/BridgeActivator.java b/http/bridge/src/main/java/org/apache/felix/http/bridge/internal/BridgeActivator.java
new file mode 100644
index 0000000..08dc565
--- /dev/null
+++ b/http/bridge/src/main/java/org/apache/felix/http/bridge/internal/BridgeActivator.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.bridge.internal;
+
+import javax.servlet.http.HttpServlet;
+import java.util.Hashtable;
+import org.apache.felix.http.base.internal.AbstractActivator;
+
+public final class BridgeActivator
+    extends AbstractActivator
+{
+    @Override
+    protected void doStart()
+        throws Exception
+    {
+        Hashtable<String, Object> props = new Hashtable<String, Object>();
+        props.put("http.felix.dispatcher", getDispatcherServlet().getClass().getName());
+        getBundleContext().registerService(HttpServlet.class.getName(), getDispatcherServlet(), props);
+    }
+
+    @Override
+    protected void doStop()
+        throws Exception
+    {
+        // Do nothing
+    }
+}
diff --git a/http/bundle/pom.xml b/http/bundle/pom.xml
new file mode 100644
index 0000000..a2f2325
--- /dev/null
+++ b/http/bundle/pom.xml
@@ -0,0 +1,98 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Bridge</name>
+    <artifactId>org.apache.felix.http.bundle</artifactId>
+    <packaging>bundle</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>
+                            org.apache.felix.http.bundle.internal.CombinedActivator
+                        </Bundle-Activator>
+                        <Export-Package>
+                            org.apache.felix.http.api;version=${pom.version};-split-package:=merge-first,
+                            org.osgi.service.http;version=1.2.0;-split-package:=merge-first,
+                            javax.servlet.*;version=2.5;-split-package:=merge-first
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.felix.http.base.*;-split-package:=merge-first,
+                            org.apache.felix.http.bridge.*,
+                            org.apache.felix.http.bundle.*,
+                            org.apache.felix.http.jetty.*,
+                            org.apache.felix.http.whiteboard.*,
+                            org.mortbay.*;-split-package:=merge-first
+                        </Private-Package>
+                        <Import-Package>
+                            javax.servlet.*,
+                            *;resolution:=optional
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.bridge</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.jetty</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.whiteboard</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/bundle/src/main/java/org/apache/felix/http/bundle/internal/CombinedActivator.java b/http/bundle/src/main/java/org/apache/felix/http/bundle/internal/CombinedActivator.java
new file mode 100644
index 0000000..d1c73fe
--- /dev/null
+++ b/http/bundle/src/main/java/org/apache/felix/http/bundle/internal/CombinedActivator.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.bundle.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.apache.felix.http.bridge.internal.BridgeActivator;
+import org.apache.felix.http.whiteboard.internal.WhiteboardActivator;
+import org.apache.felix.http.jetty.internal.JettyActivator;
+
+public final class CombinedActivator
+    implements BundleActivator
+{
+    private final static String JETTY_ENABLED_PROP = "org.apache.felix.http.jettyEnabled";
+    private final static String WHITEBOARD_ENABLED_PROP = "org.apache.felix.http.whiteboardEnabled";
+
+    private BundleActivator jettyActivator;
+    private BundleActivator bridgeActivator;
+    private BundleActivator whiteboardActivator;
+
+    public void start(BundleContext context)
+        throws Exception
+    {
+        if ("true".equals(context.getProperty(JETTY_ENABLED_PROP))) {
+            this.jettyActivator = new JettyActivator();
+        } else {
+            this.bridgeActivator = new BridgeActivator();
+        }
+
+        if ("true".equals(context.getProperty(WHITEBOARD_ENABLED_PROP))) {
+            this.whiteboardActivator = new WhiteboardActivator();
+        }
+
+        if (this.jettyActivator != null) {
+            this.jettyActivator.start(context);
+        }
+
+        if (this.bridgeActivator != null) {
+            this.bridgeActivator.start(context);
+        }
+
+        if (this.whiteboardActivator != null) {
+            this.whiteboardActivator.start(context);
+        }
+    }
+
+    public void stop(BundleContext context)
+        throws Exception
+    {
+        if (this.whiteboardActivator != null) {
+            this.whiteboardActivator.stop(context);
+        }
+
+        if (this.jettyActivator != null) {
+            this.jettyActivator.stop(context);
+        }
+
+        if (this.bridgeActivator != null) {
+            this.bridgeActivator.stop(context);
+        }
+    }
+}
diff --git a/http/jetty/pom.xml b/http/jetty/pom.xml
new file mode 100644
index 0000000..4458d19
--- /dev/null
+++ b/http/jetty/pom.xml
@@ -0,0 +1,107 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Jetty</name>
+    <artifactId>org.apache.felix.http.jetty</artifactId>
+    <packaging>bundle</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>
+                            org.apache.felix.http.jetty.internal.JettyActivator
+                        </Bundle-Activator>
+                        <Export-Package>
+                            org.apache.felix.http.api;version=${pom.version},
+                            org.osgi.service.http,
+                            javax.servlet.*;version=2.5
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.felix.http.base.*,
+                            org.apache.felix.http.jetty.*,
+                            org.mortbay.*;-split-package:=merge-first                            
+                        </Private-Package>
+                        <Import-Package>
+                            javax.net.ssl; javax.security.cert;
+                            javax.xml.parsers; org.xml.sax;
+                            org.xml.sax.helpers;
+                            org.slf4j;resolution:=optional,
+                            *;resolution:=optional
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>servlet-api-2.5</artifactId>
+            <version>6.1.14</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty</artifactId>
+            <version>6.1.14</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty-util</artifactId>
+            <version>6.1.14</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty-sslengine</artifactId>
+            <version>6.1.14</version>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.api</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.base</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyActivator.java b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyActivator.java
new file mode 100644
index 0000000..9549274
--- /dev/null
+++ b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyActivator.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.internal;
+
+import org.apache.felix.http.base.internal.AbstractActivator;
+
+public final class JettyActivator
+    extends AbstractActivator 
+{
+    private JettyService jetty;
+
+    protected void doStart()
+        throws Exception
+    {
+        this.jetty = new JettyService(getBundleContext(), getDispatcherServlet());
+        this.jetty.start();
+    }
+
+    protected void doStop()
+        throws Exception
+    {
+        this.jetty.stop();
+    }
+}
diff --git a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
new file mode 100644
index 0000000..3251331
--- /dev/null
+++ b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.internal;
+
+import org.osgi.framework.BundleContext;
+import java.util.Dictionary;
+import java.util.Properties;
+
+public final class JettyConfig
+{
+    /** Standard OSGi port property for HTTP service */
+    private static final String HTTP_PORT = "org.osgi.service.http.port";
+
+    /** Standard OSGi port property for HTTPS service */
+    private static final String HTTPS_PORT = "org.osgi.service.http.port.secure";
+
+    /** Felix specific property to enable debug messages */
+    private static final String FELIX_HTTP_DEBUG = "org.apache.felix.http.debug";
+    private static final String HTTP_DEBUG = "org.apache.felix.http.jetty.debug";
+
+    /** Felix specific property to override the keystore file location. */
+    private static final String FELIX_KEYSTORE = "org.apache.felix.https.keystore";
+    private static final String OSCAR_KEYSTORE = "org.ungoverned.osgi.bundle.https.keystore";
+
+    /** Felix specific property to override the keystore password. */
+    private static final String FELIX_KEYSTORE_PASSWORD = "org.apache.felix.https.keystore.password";
+    private static final String OSCAR_KEYSTORE_PASSWORD = "org.ungoverned.osgi.bundle.https.password";
+
+    /** Felix specific property to override the keystore key password. */
+    private static final String FELIX_KEYSTORE_KEY_PASSWORD = "org.apache.felix.https.keystore.key.password";
+    private static final String OSCAR_KEYSTORE_KEY_PASSWORD = "org.ungoverned.osgi.bundle.https.key.password";
+
+    /** Felix specific property to control whether to enable HTTPS. */
+    private static final String FELIX_HTTPS_ENABLE = "org.apache.felix.https.enable";
+    private static final String  OSCAR_HTTPS_ENABLE   = "org.ungoverned.osgi.bundle.https.enable";
+
+    /** Felix specific property to control whether to enable HTTP. */
+    private static final String FELIX_HTTP_ENABLE = "org.apache.felix.http.enable";
+
+    /** Felix specific property to override the truststore file location. */
+    private static final String FELIX_TRUSTSTORE = "org.apache.felix.https.truststore";
+
+    /** Felix specific property to override the truststore password. */
+    private static final String FELIX_TRUSTSTORE_PASSWORD = "org.apache.felix.https.truststore.password";
+
+    /** Felix specific property to control whether to want or require HTTPS client certificates. Valid values are "none", "wants", "needs". Default is "none". */
+    private static final String FELIX_HTTPS_CLIENT_CERT = "org.apache.felix.https.clientcertificate";
+    
+    private final BundleContext context;
+    private boolean debug;
+    private int httpPort;
+    private int httpsPort;
+    private String keystore;
+    private String password;
+    private String keyPassword;
+    private boolean useHttps;
+    private String truststore;
+    private String trustPassword;
+    private boolean useHttp;
+    private String clientcert;
+
+    public JettyConfig(BundleContext context)
+    {
+        this.context = context;
+        reset();
+    }
+
+    public boolean isDebug()
+    {
+        return this.debug;
+    }
+
+    public boolean isUseHttp()
+    {
+        return this.useHttp;
+    }
+
+    public boolean isUseHttps()
+    {
+        return this.useHttps;
+    }
+
+    public int getHttpPort()
+    {
+        return this.httpPort;
+    }
+
+    public int getHttpsPort()
+    {
+        return this.httpsPort;
+    }
+
+    public String getKeystore()
+    {
+        return this.keystore;
+    }
+
+    public String getPassword()
+    {
+        return this.password;
+    }
+
+    public String getTruststore()
+    {
+        return this.truststore;
+    }
+
+    public String getTrustPassword()
+    {
+        return this.trustPassword;
+    }
+
+    public String getKeyPassword()
+    {
+        return this.keyPassword;
+    }
+
+    public String getClientcert()
+    {
+        return this.clientcert;
+    }
+
+    public void reset()
+    {
+        update(null);
+    }
+
+    public void update(Dictionary props)
+    {
+        if (props == null) {
+            props = new Properties();
+        }
+
+        this.debug = getBooleanProperty(props, FELIX_HTTP_DEBUG, getBooleanProperty(props, HTTP_DEBUG, false));
+        this.httpPort = getIntProperty(props, HTTP_PORT, 8080);
+        this.httpsPort = getIntProperty(props, HTTPS_PORT, 433);
+        this.keystore = getProperty(props, FELIX_KEYSTORE, this.context.getProperty(OSCAR_KEYSTORE));
+        this.password = getProperty(props, FELIX_KEYSTORE_PASSWORD, this.context.getProperty(OSCAR_KEYSTORE_PASSWORD));
+        this.keyPassword = getProperty(props, FELIX_KEYSTORE_KEY_PASSWORD, this.context.getProperty(OSCAR_KEYSTORE_KEY_PASSWORD));
+        this.useHttps = getBooleanProperty(props, FELIX_HTTPS_ENABLE, getBooleanProperty(props, OSCAR_HTTPS_ENABLE, false));
+        this.useHttp = getBooleanProperty(props, FELIX_HTTP_ENABLE, true);
+        this.truststore = getProperty(props, FELIX_TRUSTSTORE, null);
+        this.trustPassword = getProperty(props, FELIX_TRUSTSTORE_PASSWORD, null);
+        this.clientcert = getProperty(props, FELIX_HTTPS_CLIENT_CERT, "none");
+    }
+
+    private String getProperty(Dictionary props, String name, String defValue)
+    {
+        String value = (String)props.get(name);
+        if (value == null) {
+            value = this.context.getProperty(name);
+        }
+
+        return value != null ? value : defValue;
+    }
+
+    private boolean getBooleanProperty(Dictionary props, String name, boolean defValue)
+    {
+        String value = getProperty(props, name, null);
+        if (value != null) {
+            return (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes"));
+        }
+
+        return defValue;
+    }
+
+    private int getIntProperty(Dictionary props, String name, int defValue)
+    {
+        try {
+            return Integer.parseInt(getProperty(props, name, null));
+        } catch (Exception e) {
+            return defValue;
+        }
+    }
+}
diff --git a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyLogger.java b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyLogger.java
new file mode 100644
index 0000000..13f025c
--- /dev/null
+++ b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyLogger.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.internal;
+
+import org.mortbay.log.Logger;
+import org.apache.felix.http.base.internal.util.SystemLogger;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public final class JettyLogger
+    implements Logger
+{
+    private final static Map<String, Logger> LOGGERS =
+        new HashMap<String, Logger>();
+
+    private final String name;
+    private boolean debugEnabled;
+
+    public JettyLogger()
+    {
+        this("org.mortbay.log");
+    }
+
+    public JettyLogger(String name)
+    {
+        this.name = name;
+    }
+
+    public org.mortbay.log.Logger getLogger(String name)
+    {
+        Logger logger = LOGGERS.get(name);
+        if (logger == null) {
+            logger = new JettyLogger(name);
+            logger.setDebugEnabled(isDebugEnabled());
+            LOGGERS.put(name, logger);
+        }
+
+        return logger;
+    }
+
+    public boolean isDebugEnabled()
+    {
+        return this.debugEnabled;
+    }
+
+    public void setDebugEnabled(boolean enabled)
+    {
+        this.debugEnabled = enabled;
+    }
+
+    public void debug(String msg, Throwable cause)
+    {
+        SystemLogger.get().debug(msg);
+    }
+
+    public void debug(String msg, Object arg0, Object arg1)
+    {
+        SystemLogger.get().debug(format(msg, arg0, arg1));
+    }
+
+    public void info(String msg, Object arg0, Object arg1)
+    {
+        SystemLogger.get().info(format(msg, arg0, arg1));
+    }
+
+    public void warn(String msg, Throwable cause)
+    {
+        SystemLogger.get().warning(msg, cause);
+    }
+
+    public void warn( String msg, Object arg0, Object arg1 )
+    {
+        SystemLogger.get().warning(format(msg, arg0, arg1), null);
+    }
+
+    public String toString()
+    {
+        return this.name;
+    }
+
+    private String format(String msg, Object arg0, Object arg1)
+    {
+        int i0 = msg.indexOf("{}");
+        int i1 = i0 < 0 ? -1 : msg.indexOf("{}", i0 + 2);
+
+        if (arg1 != null && i1 >= 0) {
+            msg = msg.substring(0, i1) + arg1 + msg.substring(i1 + 2);
+        }
+
+        if (arg0 != null && i0 >= 0) {
+            msg = msg.substring(0, i0) + arg0 + msg.substring(i0 + 2);
+        }
+
+        return msg;
+    }
+}
diff --git a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
new file mode 100644
index 0000000..7cfffe9
--- /dev/null
+++ b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.jetty.internal;
+
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.mortbay.jetty.security.HashUserRealm;
+import org.mortbay.jetty.security.SslSelectChannelConnector;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.servlet.*;
+import org.mortbay.log.Log;
+import org.mortbay.log.StdErrLog;
+import org.apache.felix.http.base.internal.util.SystemLogger;
+import org.apache.felix.http.base.internal.DispatcherServlet;
+import java.util.Properties;
+import java.util.Dictionary;
+
+public final class JettyService
+    implements ManagedService, Runnable
+{
+    /** PID for configuration of the HTTP service. */
+    private static final String PID = "org.apache.felix.http";
+
+    private final JettyConfig config;
+    private final BundleContext context;
+    private boolean running;
+    private Thread thread;
+    private ServiceRegistration configServiceReg;
+    private Server server;
+    private DispatcherServlet dispatcher;
+
+    public JettyService(BundleContext context, DispatcherServlet dispatcher)
+    {
+        this.context = context;
+        this.config = new JettyConfig(this.context);
+        this.dispatcher = dispatcher;
+    }
+    
+    public void start()
+        throws Exception
+    {
+        this.running = true;
+        this.thread = new Thread(this, "Jetty HTTP Service");
+        this.thread.start();
+
+        Properties props = new Properties();
+        props.put(Constants.SERVICE_PID, PID);
+
+        this.configServiceReg = this.context.registerService(ManagedService.class.getName(), this, props);
+    }
+
+    public void stop()
+        throws Exception
+    {
+        if (this.configServiceReg != null) {
+            this.configServiceReg.unregister();
+        }
+
+        this.running = false;
+        this.thread.interrupt();
+
+        try {
+            this.thread.join(3000);
+        } catch (InterruptedException e) {
+            // Do nothing
+        }
+    }
+    
+    public void updated(Dictionary props)
+        throws ConfigurationException
+    {
+        this.config.update(props);
+        if (this.thread != null) {
+            this.thread.interrupt();
+        }
+    }
+
+    private void startJetty()
+    {
+        try {
+            initializeJetty();
+        } catch (Exception e) {
+            SystemLogger.get().error("Exception while initializing Jetty.", e);
+        }
+    }
+
+    private void stopJetty()
+    {
+        try {
+            this.server.stop();
+        } catch (Exception e) {
+            SystemLogger.get().error("Exception while stopping Jetty.", e);
+        }
+    }
+
+    protected void initializeJettyLogger()
+    {
+        Log.setLog(new JettyLogger());
+    }
+
+    private void destroyJettyLogger()
+    {
+        Log.setLog(new StdErrLog());
+    }
+
+    private void initializeJetty()
+        throws Exception
+    {
+        HashUserRealm realm = new HashUserRealm("OSGi HTTP Service Realm");
+        this.server = new Server();
+        this.server.addUserRealm(realm);
+
+        if (this.config.isUseHttp()) {
+            initializeHttp();
+        }
+
+        if (this.config.isUseHttps()) {
+            initializeHttps();
+        }
+
+        Context context = new Context(this.server, "/", Context.SESSIONS);
+        context.addServlet(new ServletHolder(this.dispatcher), "/*");
+
+        this.server.start();
+    }
+
+    private void initializeHttp()
+        throws Exception
+    {
+        Connector connector = new SelectChannelConnector();
+        connector.setPort(this.config.getHttpPort());
+        connector.setMaxIdleTime(60000);
+        this.server.addConnector(connector);
+    }
+
+    private void initializeHttps()
+        throws Exception
+    {
+        SslSelectChannelConnector connector = new SslSelectChannelConnector();
+        connector.setPort(this.config.getHttpsPort());
+        connector.setMaxIdleTime(60000);
+        
+        if (this.config.getKeystore() != null) {
+            connector.setKeystore(this.config.getKeystore());
+        }
+        
+        if (this.config.getPassword() != null) {
+            System.setProperty(SslSelectChannelConnector.PASSWORD_PROPERTY, this.config.getPassword());
+            connector.setPassword(this.config.getPassword());
+        }
+        
+        if (this.config.getKeyPassword() != null) {
+            System.setProperty(SslSelectChannelConnector.KEYPASSWORD_PROPERTY, this.config.getKeyPassword());
+            connector.setKeyPassword(this.config.getKeyPassword());
+        }
+        
+        if (this.config.getTruststore() != null) {
+            connector.setTruststore(this.config.getTruststore());
+        }
+        
+        if (this.config.getTrustPassword() != null) {
+            connector.setTrustPassword(this.config.getTrustPassword());
+        }
+        
+        if ("wants".equals(this.config.getClientcert())) {
+            connector.setWantClientAuth(true);
+        } else if ("needs".equals(this.config.getClientcert())) {
+            connector.setNeedClientAuth(true);
+        }
+
+        this.server.addConnector(connector);
+    }
+
+    public void run()
+    {
+        Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+
+        while (this.running) {
+            initializeJettyLogger();
+            startJetty();
+
+            synchronized (this) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    // we will definitely be interrupted
+                }
+            }
+
+            stopJetty();
+            destroyJettyLogger();
+        }
+    }
+}
diff --git a/http/pom.xml b/http/pom.xml
new file mode 100644
index 0000000..5d35c62
--- /dev/null
+++ b/http/pom.xml
@@ -0,0 +1,123 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>felix-parent</artifactId>
+        <version>1.2.0</version>
+    </parent>
+
+    <artifactId>org.apache.felix.http</artifactId>
+    <packaging>pom</packaging>
+    <name>Apache Felix Http</name>
+    <version>2.0.0-SNAPSHOT</version>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+            <plugins>
+                <plugin>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <configuration>
+                        <source>1.5</source>
+                        <target>1.5</target>
+                        <compilerVersion>1.5</compilerVersion>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>2.0.0</version>
+                    <extensions>true</extensions>
+                    <configuration>
+                        <instructions>
+                            <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+                            <Bundle-Version>${pom.version}</Bundle-Version>
+                        </instructions>
+                    </configuration>
+                </plugin>
+            </plugins>
+    </build>
+
+    <modules>
+        <module>api</module>
+        <module>base</module>
+        <module>bridge</module>
+        <module>jetty</module>
+        <module>proxy</module>
+        <module>whiteboard</module>
+        <module>bundle</module>
+        <module>samples/filter</module>
+        <module>samples/bridge</module>
+        <module>samples/whiteboard</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.8.0</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>javax.servlet</groupId>
+                <artifactId>servlet-api</artifactId>
+                <version>2.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>org.osgi.core</artifactId>
+                <version>1.2.0</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>org.osgi.foundation</artifactId>
+                <version>1.2.0</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>org.osgi.compendium</artifactId>
+                <version>1.2.0</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.apache.felix</groupId>
+                        <artifactId>javax.servlet</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+</project>
diff --git a/http/proxy/pom.xml b/http/proxy/pom.xml
new file mode 100644
index 0000000..4b7661f
--- /dev/null
+++ b/http/proxy/pom.xml
@@ -0,0 +1,47 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Proxy</name>
+    <artifactId>org.apache.felix.http.proxy</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/proxy/src/main/java/org/apache/felix/http/proxy/DispatcherTracker.java b/http/proxy/src/main/java/org/apache/felix/http/proxy/DispatcherTracker.java
new file mode 100644
index 0000000..7552dc9
--- /dev/null
+++ b/http/proxy/src/main/java/org/apache/felix/http/proxy/DispatcherTracker.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.proxy;
+
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Filter;
+import org.osgi.framework.Constants;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.ServletConfig;
+
+public final class DispatcherTracker
+    extends ServiceTracker
+{
+    private final static String DEFAULT_FILTER = "(http.felix.dispatcher=*)";
+
+    private final ServletConfig config;
+    private HttpServlet dispatcher;
+    
+    public DispatcherTracker(BundleContext context, String filter, ServletConfig config)
+        throws Exception
+    {
+        super(context, createFilter(context, filter), null);
+        this.config = config;
+    }
+
+    public HttpServlet getDispatcher()
+    {
+        return this.dispatcher;
+    }
+
+    @Override
+    public Object addingService(ServiceReference ref)
+    {
+        Object service = super.addingService(ref);
+        if (service instanceof HttpServlet) {
+            setDispatcher((HttpServlet)service);
+        }
+
+        return service;
+    }
+
+    @Override
+    public void removedService(ServiceReference ref, Object service)
+    {
+        if (service instanceof HttpServlet) {
+            setDispatcher(null);
+        }
+
+        super.removedService(ref, service);
+    }
+
+    private void log(String message, Throwable cause)
+    {
+        this.config.getServletContext().log(message, cause);
+    }
+
+    private void setDispatcher(HttpServlet dispatcher)
+    {
+        destroyDispatcher();
+        this.dispatcher = dispatcher;
+        initDispatcher();
+    }
+
+    private void destroyDispatcher()
+    {
+        if (this.dispatcher == null) {
+            return;
+        }
+
+        this.dispatcher.destroy();
+        this.dispatcher = null;
+    }
+
+    private void initDispatcher()
+    {
+        if (this.dispatcher == null) {
+            return;
+        }
+
+        try {
+            this.dispatcher.init(this.config);
+        } catch (Exception e) {
+            log("Failed to initialize dispatcher", e);
+        }
+    }
+
+    private static Filter createFilter(BundleContext context, String filter)
+        throws Exception
+    {
+        StringBuffer str = new StringBuffer();
+        str.append("(&(").append(Constants.OBJECTCLASS).append("=");
+        str.append(HttpServlet.class.getName()).append(")");
+        str.append(filter != null ? filter : DEFAULT_FILTER).append(")");
+        return context.createFilter(str.toString());
+    }
+}
diff --git a/http/proxy/src/main/java/org/apache/felix/http/proxy/ProxyServlet.java b/http/proxy/src/main/java/org/apache/felix/http/proxy/ProxyServlet.java
new file mode 100644
index 0000000..06d49d9
--- /dev/null
+++ b/http/proxy/src/main/java/org/apache/felix/http/proxy/ProxyServlet.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.proxy;
+
+import org.osgi.framework.BundleContext;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+import javax.servlet.ServletConfig;
+import java.io.IOException;
+
+public final class ProxyServlet
+    extends HttpServlet
+{
+    private DispatcherTracker tracker;
+
+    @Override
+    public void init(ServletConfig config)
+        throws ServletException
+    {
+        super.init(config);
+
+        try {
+            doInit();
+        } catch (ServletException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ServletException(e);
+        }
+    }
+
+    private void doInit()
+        throws Exception
+    {
+        this.tracker = new DispatcherTracker(getBundleContext(), null, getServletConfig());
+        this.tracker.open();
+    }
+
+    @Override
+    protected void service(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        HttpServlet dispatcher = this.tracker.getDispatcher();
+        if (dispatcher != null) {
+            dispatcher.service(req, res);
+        } else {
+            res.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void destroy()
+    {
+        this.tracker.close();
+        super.destroy();
+    }
+
+    private BundleContext getBundleContext()
+        throws ServletException
+    {
+        Object context = getServletContext().getAttribute(BundleContext.class.getName());
+        if (context instanceof BundleContext) {
+            return (BundleContext)context;
+        }
+
+        throw new ServletException("Bundle context attribute [" + BundleContext.class.getName() +
+                "] not set in servlet context");
+    }
+}
diff --git a/http/samples/bridge/pom.xml b/http/samples/bridge/pom.xml
new file mode 100644
index 0000000..d96f14e
--- /dev/null
+++ b/http/samples/bridge/pom.xml
@@ -0,0 +1,118 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Samples - Bridge</name>
+    <artifactId>org.apache.felix.http.samples.bridge</artifactId>
+    <packaging>war</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>maven-jetty-plugin</artifactId>
+                <version>6.1.10</version>
+                <configuration>
+                    <contextPath>/</contextPath>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-bundles</id>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.apache.felix</groupId>
+                                    <artifactId>org.apache.felix.http.bundle</artifactId>
+                                    <version>${pom.version}</version>
+                                </artifactItem>
+                                <artifactItem>
+                                    <groupId>org.apache.felix</groupId>
+                                    <artifactId>org.apache.felix.http.samples.filter</artifactId>
+                                    <version>${pom.version}</version>
+                                </artifactItem>
+                                <artifactItem>
+                                    <groupId>org.apache.felix</groupId>
+                                    <artifactId>org.apache.felix.webconsole</artifactId>
+                                    <version>1.2.8</version>
+                                </artifactItem>
+                            </artifactItems>
+                            <stripVersion>true</stripVersion>
+                            <outputDirectory>
+                                ${project.build.directory}/bundles
+                            </outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <webResources>
+                        <resource>
+                            <directory>
+                                ${project.build.directory}/bundles
+                            </directory>
+                            <targetPath>WEB-INF/bundles</targetPath>
+                        </resource>
+                    </webResources>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <version>1.8.1</version>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.proxy</artifactId>
+            <version>${pom.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/FrameworkService.java b/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/FrameworkService.java
new file mode 100644
index 0000000..cb66857
--- /dev/null
+++ b/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/FrameworkService.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.bridge;
+
+import org.apache.felix.framework.Felix;
+import org.apache.felix.framework.util.FelixConstants;
+import javax.servlet.ServletContext;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.Arrays;
+
+public final class FrameworkService
+{
+    private final ServletContext context;
+    private Felix felix;
+
+    public FrameworkService(ServletContext context)
+    {
+        this.context = context;
+    }
+
+    public void start()
+    {
+        try {
+            doStart();
+        } catch (Exception e) {
+            log("Failed to start framework", e);
+        }
+    }
+
+    public void stop()
+    {
+        try {
+            doStop();
+        } catch (Exception e) {
+            log("Error stopping framework", e);
+        }
+    }
+
+    private void doStart()
+        throws Exception
+    {
+        Felix tmp = new Felix(createConfig());
+        tmp.start();
+        this.felix = tmp;
+        log("OSGi framework started", null);
+    }
+
+    private void doStop()
+        throws Exception
+    {
+        if (this.felix != null) {
+            this.felix.stop();
+        }
+
+        log("OSGi framework stopped", null);
+    }
+
+    private Map<String, Object> createConfig()
+        throws Exception
+    {
+        Properties props = new Properties();
+        props.load(this.context.getResourceAsStream("/WEB-INF/framework.properties"));
+
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        for (Object key : props.keySet()) {
+            map.put(key.toString(), props.get(key));
+        }
+        
+        map.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, Arrays.asList(new ProvisionActivator(this.context)));
+        return map;
+    }
+
+    private void log(String message, Throwable cause)
+    {
+        this.context.log(message, cause);
+    }
+}
diff --git a/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/ProvisionActivator.java b/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/ProvisionActivator.java
new file mode 100644
index 0000000..a85cebe
--- /dev/null
+++ b/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/ProvisionActivator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.bridge;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Bundle;
+import javax.servlet.ServletContext;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class ProvisionActivator
+    implements BundleActivator
+{
+    private final ServletContext servletContext;
+
+    public ProvisionActivator(ServletContext servletContext)
+    {
+        this.servletContext = servletContext;
+    }
+
+    public void start(BundleContext context)
+        throws Exception
+    {
+        servletContext.setAttribute(BundleContext.class.getName(), context);
+
+        ArrayList<Bundle> installed = new ArrayList<Bundle>();
+        for (URL url : findBundles()) {
+            this.servletContext.log("Installing bundle [" + url + "]");
+            Bundle bundle = context.installBundle(url.toExternalForm());
+            installed.add(bundle);
+        }
+
+        for (Bundle bundle : installed) {
+            bundle.start();
+        }
+    }
+
+    public void stop(BundleContext context)
+        throws Exception
+    {
+    }
+
+    private List<URL> findBundles()
+        throws Exception
+    {
+        ArrayList<URL> list = new ArrayList<URL>();
+        for (Object o : this.servletContext.getResourcePaths("/WEB-INF/bundles/")) {
+            String name = (String)o;
+            if (name.endsWith(".jar")) {
+                URL url = this.servletContext.getResource(name);
+                if (url != null) {
+                    list.add(url);
+                }
+            }
+        }
+
+        return list;
+    }
+}
diff --git a/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/StartupListener.java b/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/StartupListener.java
new file mode 100644
index 0000000..1b7ad60
--- /dev/null
+++ b/http/samples/bridge/src/main/java/org/apache/felix/http/samples/bridge/StartupListener.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.bridge;
+
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletContextEvent;
+
+public final class StartupListener
+    implements ServletContextListener
+{
+    private FrameworkService service;
+
+    public void contextInitialized(ServletContextEvent event)
+    {
+        this.service = new FrameworkService(event.getServletContext());
+        this.service.start();
+    }
+
+    public void contextDestroyed(ServletContextEvent event)
+    {
+        this.service.stop();
+    }
+}
diff --git a/http/samples/bridge/src/main/webapp/WEB-INF/framework.properties b/http/samples/bridge/src/main/webapp/WEB-INF/framework.properties
new file mode 100644
index 0000000..48ec827
--- /dev/null
+++ b/http/samples/bridge/src/main/webapp/WEB-INF/framework.properties
@@ -0,0 +1,162 @@
+org.osgi.framework.storage.clean = onFirstInit
+
+org.osgi.framework.system.packages = \
+   org.osgi.framework; version=1.4.0, \
+   org.osgi.framework.hooks.service, \
+   org.osgi.framework.launch, \
+   org.osgi.service.condpermadmin; version=1.0.0, \
+   org.osgi.service.packageadmin; version=1.2.0, \
+   org.osgi.service.permissionadmin; version=1.2.0, \
+   org.osgi.service.startlevel; version=1.1.0, \
+   org.osgi.service.url; version=1.0.0,\
+ org.osgi.service.application;version="1.0", \
+ org.osgi.service.cm;version="1.2", \
+ org.osgi.service.component;version="1.0", \
+ org.osgi.service.deploymentadmin;version="1.0", \
+ org.osgi.service.deploymentadmin.spi;uses:="org.osgi.service.deploymentadmin";version="1.0", \
+ org.osgi.service.device;version="1.1", \
+ org.osgi.service.event;version="1.1", \
+ org.osgi.service.http;version="1.2", \
+ org.osgi.service.io;version="1.0", \
+ org.osgi.service.log;version="1.3", \
+ org.osgi.service.metatype;version="1.1", \
+ org.osgi.service.monitor;version="1.0", \
+ org.osgi.service.prefs;version="1.1", \
+ org.osgi.service.provisioning;version="1.1", \
+ org.osgi.service.upnp;version="1.1", \
+ org.osgi.service.useradmin;version="1.1", \
+ org.osgi.service.wireadmin;version="1.0", \
+ info.dmtree.notification;version="1.0", \
+ info.dmtree.notification.spi;uses:="info.dmtree.notification";version="1.0", \
+ info.dmtree.registry;uses:="info.dmtree.notification";version="1.0", \
+ info.dmtree.security;version="1.0", \
+ info.dmtree.spi;version="1.0", \
+ org.osgi.util.gsm;version="1.0", \
+ org.osgi.util.measurement;version="1.0", \
+ org.osgi.util.mobile;version="1.0", \
+ org.osgi.util.position;uses:="org.osgi.util.measurement";version="1.0", \
+ org.osgi.util.tracker;version="1.3.3", \
+ org.osgi.util.xml;version="1.0",\
+ javax.accessibility,\
+ javax.activity,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml,\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.xpath,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers,\
+ javax.servlet;javax.servlet.http;version=2.5
diff --git a/http/samples/bridge/src/main/webapp/WEB-INF/web.xml b/http/samples/bridge/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..69f840c
--- /dev/null
+++ b/http/samples/bridge/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+    "http://java.sun.com/dtd/web-app_2_3.dtd">
+<web-app>
+
+    <listener>
+        <listener-class>org.apache.felix.http.samples.bridge.StartupListener</listener-class>
+    </listener>
+
+    <servlet>
+        <servlet-name>proxy</servlet-name>
+        <servlet-class>org.apache.felix.http.proxy.ProxyServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>proxy</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/http/samples/filter/pom.xml b/http/samples/filter/pom.xml
new file mode 100644
index 0000000..aeac93c
--- /dev/null
+++ b/http/samples/filter/pom.xml
@@ -0,0 +1,75 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Samples - Filter</name>
+    <artifactId>org.apache.felix.http.samples.filter</artifactId>
+    <packaging>bundle</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>
+                            org.apache.felix.http.samples.filter.Activator
+                        </Bundle-Activator>
+                        <Private-Package>
+                            org.apache.felix.http.samples.filter.*
+                        </Private-Package>
+                        <Import-Package>
+                            *;resolution:=optional
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.api</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/Activator.java b/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/Activator.java
new file mode 100644
index 0000000..e6e2f9d
--- /dev/null
+++ b/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/Activator.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.filter;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.apache.felix.http.api.ExtHttpService;
+
+public final class Activator
+    implements BundleActivator
+{
+    private ServiceTracker tracker;
+    private TestServlet servlet1 = new TestServlet("servlet1");
+    private TestServlet servlet2 = new TestServlet("servlet2");
+    private TestFilter filter1 = new TestFilter("filter1");
+    private TestFilter filter2 = new TestFilter("filter2");
+
+    public void start(BundleContext context)
+        throws Exception
+    {
+        this.tracker = new ServiceTracker(context, ExtHttpService.class.getName(), null)
+        {
+            @Override
+            public Object addingService(ServiceReference ref)
+            {
+                Object service =  super.addingService(ref);
+                serviceAdded((ExtHttpService)service);
+                return service;
+            }
+
+            @Override
+            public void removedService(ServiceReference ref, Object service)
+            {
+                serviceRemoved((ExtHttpService)service);
+                super.removedService(ref, service);
+            }
+        };
+
+        this.tracker.open();
+    }
+
+    public void stop(BundleContext context)
+        throws Exception
+    {
+        this.tracker.close();
+    }
+
+    private void serviceAdded(ExtHttpService service)
+    {
+        try {
+            service.registerServlet("/", this.servlet1, null, null);
+            service.registerServlet("/other", this.servlet2, null, null);
+            service.registerFilter(this.filter1, ".*", null, 0, null);
+            service.registerFilter(this.filter2, "/other/.*", null, 100, null);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void serviceRemoved(ExtHttpService service)
+    {
+        service.unregisterServlet(this.servlet1);
+        service.unregisterServlet(this.servlet2);
+        service.unregisterFilter(this.filter1);
+        service.unregisterFilter(this.filter2);
+    }
+}
diff --git a/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/TestFilter.java b/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/TestFilter.java
new file mode 100644
index 0000000..7dd50a7
--- /dev/null
+++ b/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/TestFilter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.filter;
+
+import javax.servlet.*;
+import java.io.IOException;
+
+public class TestFilter
+    implements Filter
+{
+    private final String name;
+
+    public TestFilter(String name)
+    {
+        this.name = name;
+    }
+    
+    public void init(FilterConfig config)
+        throws ServletException
+    {
+        doLog("Init with config [" + config + "]");
+    }
+
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+        throws IOException, ServletException
+    {
+        doLog("Filter request [" + req + "]");
+        chain.doFilter(req, res);
+    }
+
+    public void destroy()
+    {
+        doLog("Destroyed filter");
+    }
+
+    private void doLog(String message)
+    {
+        System.out.println("## [" + this.name + "] " + message);
+    }
+}
diff --git a/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/TestServlet.java b/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/TestServlet.java
new file mode 100644
index 0000000..801513d
--- /dev/null
+++ b/http/samples/filter/src/main/java/org/apache/felix/http/samples/filter/TestServlet.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.filter;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class TestServlet
+    extends HttpServlet
+{
+    private final String name;
+
+    public TestServlet(String name)
+    {
+        this.name = name;
+    }
+    
+    @Override
+    public void init(ServletConfig config)
+        throws ServletException
+    {
+        doLog("Init with config [" + config + "]");
+        super.init(config);  
+    }
+
+    @Override
+    public void destroy()
+    {
+        doLog("Destroyed servlet");
+        super.destroy();
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        res.setContentType("text/plain");
+        PrintWriter out = res.getWriter();
+
+        out.println("Request = " + req);
+        out.println("PathInfo = " + req.getPathInfo());
+    }
+
+    private void doLog(String message)
+    {
+        System.out.println("## [" + this.name + "] " + message);
+    }
+}
diff --git a/http/samples/whiteboard/pom.xml b/http/samples/whiteboard/pom.xml
new file mode 100644
index 0000000..7383135
--- /dev/null
+++ b/http/samples/whiteboard/pom.xml
@@ -0,0 +1,69 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Samples - Whiteboard</name>
+    <artifactId>org.apache.felix.http.samples.whiteboard</artifactId>
+    <packaging>bundle</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>
+                            org.apache.felix.http.samples.whiteboard.Activator
+                        </Bundle-Activator>
+                        <Private-Package>
+                            org.apache.felix.http.samples.whiteboard.*
+                        </Private-Package>
+                        <Import-Package>
+                            *;resolution:=optional
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/Activator.java b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/Activator.java
new file mode 100644
index 0000000..4722a7c
--- /dev/null
+++ b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/Activator.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.whiteboard;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import javax.servlet.Servlet;
+import javax.servlet.Filter;
+import java.util.Hashtable;
+
+public final class Activator
+    implements BundleActivator
+{
+    private ServiceRegistration reg1;
+    private ServiceRegistration reg2;
+    private ServiceRegistration reg3;
+    private ServiceRegistration reg4;
+    
+    public void start(BundleContext context)
+        throws Exception
+    {
+        Hashtable<String, String> props = new Hashtable<String, String>();
+        props.put("alias", "/");
+        this.reg1 = context.registerService(Servlet.class.getName(), new TestServlet("servlet1"), props);
+
+        props = new Hashtable<String, String>();
+        props.put("alias", "/other");
+        this.reg2 = context.registerService(Servlet.class.getName(), new TestServlet("servlet2"), props);
+
+        props = new Hashtable<String, String>();
+        props.put("pattern", ".*");
+        this.reg3 = context.registerService(Filter.class.getName(), new TestFilter("filter1"), props);
+
+        props = new Hashtable<String, String>();
+        props.put("pattern", "/other/.*");
+        props.put("service.ranking", "100");
+        this.reg4 = context.registerService(Filter.class.getName(), new TestFilter("filter2"), props);
+    }
+
+    public void stop(BundleContext context)
+        throws Exception
+    {
+        this.reg1.unregister();
+        this.reg2.unregister();
+        this.reg3.unregister();
+        this.reg4.unregister();
+    }
+}
diff --git a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestFilter.java b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestFilter.java
new file mode 100644
index 0000000..9e50609
--- /dev/null
+++ b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestFilter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.whiteboard;
+
+import javax.servlet.*;
+import java.io.IOException;
+
+public class TestFilter
+    implements Filter
+{
+    private final String name;
+
+    public TestFilter(String name)
+    {
+        this.name = name;
+    }
+
+    public void init(FilterConfig config)
+        throws ServletException
+    {
+        doLog("Init with config [" + config + "]");
+    }
+
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+        throws IOException, ServletException
+    {
+        doLog("Filter request [" + req + "]");
+        chain.doFilter(req, res);
+    }
+
+    public void destroy()
+    {
+        doLog("Destroyed filter");
+    }
+
+    private void doLog(String message)
+    {
+        System.out.println("## [" + this.name + "] " + message);
+    }
+}
\ No newline at end of file
diff --git a/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestServlet.java b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestServlet.java
new file mode 100644
index 0000000..43c367d
--- /dev/null
+++ b/http/samples/whiteboard/src/main/java/org/apache/felix/http/samples/whiteboard/TestServlet.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.samples.whiteboard;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class TestServlet
+    extends HttpServlet
+{
+    private final String name;
+
+    public TestServlet(String name)
+    {
+        this.name = name;
+    }
+
+    @Override
+    public void init(ServletConfig config)
+        throws ServletException
+    {
+        doLog("Init with config [" + config + "]");
+        super.init(config);
+    }
+
+    @Override
+    public void destroy()
+    {
+        doLog("Destroyed servlet");
+        super.destroy();
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException
+    {
+        res.setContentType("text/plain");
+        PrintWriter out = res.getWriter();
+
+        out.println("Request = " + req);
+        out.println("PathInfo = " + req.getPathInfo());
+    }
+
+    private void doLog(String message)
+    {
+        System.out.println("## [" + this.name + "] " + message);
+    }
+}
\ No newline at end of file
diff --git a/http/whiteboard/pom.xml b/http/whiteboard/pom.xml
new file mode 100644
index 0000000..1b49f15
--- /dev/null
+++ b/http/whiteboard/pom.xml
@@ -0,0 +1,77 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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.apache.felix</groupId>
+        <artifactId>org.apache.felix.http</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <name>Apache Felix Http Whiteboard</name>
+    <artifactId>org.apache.felix.http.whiteboard</artifactId>
+    <packaging>bundle</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>
+                            org.apache.felix.http.whiteboard.internal.WhiteboardActivator
+                        </Bundle-Activator>
+                        <Private-Package>
+                            org.apache.felix.http.whiteboard.*
+                        </Private-Package>
+                        <Import-Package>
+                            javax.servlet.*,
+                            org.osgi.service.http.*,
+                            *;resolution:=optional
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${pom.groupId}</groupId>
+            <artifactId>org.apache.felix.http.api</artifactId>
+            <version>${pom.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
new file mode 100644
index 0000000..0ffb830
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+import org.apache.felix.http.whiteboard.internal.tracker.FilterTracker;
+import org.apache.felix.http.whiteboard.internal.tracker.HttpContextTracker;
+import org.apache.felix.http.whiteboard.internal.tracker.ServletTracker;
+import org.apache.felix.http.whiteboard.internal.tracker.HttpServiceTracker;
+import org.apache.felix.http.whiteboard.internal.manager.ExtenderManagerImpl;
+import org.apache.felix.http.whiteboard.internal.manager.ExtenderManager;
+import org.apache.felix.http.whiteboard.internal.util.SystemLogger;
+import java.util.ArrayList;
+
+public final class WhiteboardActivator
+    implements BundleActivator
+{
+    private final ArrayList<ServiceTracker> trackers;
+    private ExtenderManager manager;
+
+    public WhiteboardActivator()
+    {
+        this.trackers = new ArrayList<ServiceTracker>();
+    }
+
+    public void start(BundleContext context)
+        throws Exception
+    {
+        SystemLogger.get().open(context);
+        this.manager = new ExtenderManagerImpl();
+        addTracker(new HttpContextTracker(context, this.manager));
+        addTracker(new FilterTracker(context, this.manager));
+        addTracker(new ServletTracker(context, this.manager));
+        addTracker(new HttpServiceTracker(context, this.manager));
+    }
+
+    private void addTracker(ServiceTracker tracker)
+    {
+        this.trackers.add(tracker);
+        tracker.open();
+    }
+
+    public void stop(BundleContext context)
+        throws Exception
+    {
+        for (ServiceTracker tracker : this.trackers) {
+            tracker.close();
+        }
+
+        this.trackers.clear();
+        this.manager.unregisterAll();
+        SystemLogger.get().close();
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java
new file mode 100644
index 0000000..e503ff2
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.manager;
+
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import java.util.Hashtable;
+
+public abstract class AbstractMapping
+{
+    private final HttpContext context;
+    private final Hashtable<String, String> initParams;
+
+    public AbstractMapping(HttpContext context)
+    {
+        this.context = context;
+        this.initParams = new Hashtable<String, String>();
+    }
+
+    public final HttpContext getContext()
+    {
+        return this.context;
+    }
+
+    public final Hashtable<String, String> getInitParams()
+    {
+        return this.initParams;
+    }
+
+    public abstract void register(HttpService httpService);
+
+    public abstract void unregister(HttpService httpService);
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/DefaultHttpContext.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/DefaultHttpContext.java
new file mode 100644
index 0000000..435fb4d
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/DefaultHttpContext.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.manager;
+
+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 bundle;
+
+    public DefaultHttpContext(Bundle bundle)
+    {
+        this.bundle = bundle;
+    }
+
+    public String getMimeType(String name)
+    {
+        return null;
+    }
+
+    public URL getResource(String name)
+    {
+        if (name.startsWith("/")) {
+            name = name.substring(1);
+        }
+
+        return this.bundle.getResource(name);
+    }
+
+    public boolean handleSecurity(HttpServletRequest req, HttpServletResponse res)
+    {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java
new file mode 100644
index 0000000..0e0378e
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.manager;
+
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.log.LogService;
+import org.osgi.framework.ServiceReference;
+import javax.servlet.Servlet;
+import javax.servlet.Filter;
+
+public interface ExtenderManager
+{
+    public void add(HttpContext service, ServiceReference ref);
+
+    public void remove(HttpContext service);
+
+    public void add(Filter service, ServiceReference ref);
+
+    public void remove(Filter service);
+
+    public void add(Servlet service, ServiceReference ref);
+
+    public void remove(Servlet service);
+
+    public void setHttpService(HttpService service);
+
+    public void unsetHttpService();
+
+    public void unregisterAll();
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
new file mode 100644
index 0000000..e144e1e
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.manager;
+
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.apache.felix.http.api.ExtHttpService;
+import org.apache.felix.http.whiteboard.internal.util.SystemLogger;
+
+import javax.servlet.Servlet;
+import javax.servlet.Filter;
+import java.util.HashMap;
+
+public final class ExtenderManagerImpl
+    implements ExtenderManager
+{
+    private final static String CONTEXT_ID_KEY = "contextId";
+    private final static String PATTERN_KEY = "pattern";
+    private final static String ALIAS_KEY = "alias";
+    private final static String INIT_KEY_PREFIX = "init.";
+
+    private HttpService httpService;
+    private final HashMap<Object, AbstractMapping> mapping;
+    private final HttpContextManager contextManager;
+
+    public ExtenderManagerImpl()
+    {
+        this.mapping = new HashMap<Object, AbstractMapping>();
+        this.contextManager = new HttpContextManager();
+    }
+
+    private String getStringProperty(ServiceReference ref, String key)
+    {
+        Object value = ref.getProperty(key);
+        return (value instanceof String) ? (String)value : null;
+    }
+
+    private 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;
+        }
+    }
+
+    private void addInitParams(ServiceReference ref, AbstractMapping mapping)
+    {
+        for (String key : ref.getPropertyKeys()) {
+            if (key.startsWith(INIT_KEY_PREFIX)) {
+                String paramKey = key.substring(INIT_KEY_PREFIX.length());
+                String paramValue = getStringProperty(ref, key);
+
+                if (paramValue != null) {
+                    mapping.getInitParams().put(paramKey, paramValue);
+                }
+            }
+        }
+    }
+
+    public void add(HttpContext service, ServiceReference ref)
+    {
+        Bundle bundle = ref.getBundle();
+        String contextId = getStringProperty(ref, CONTEXT_ID_KEY);
+        if (contextId != null) {
+            this.contextManager.addHttpContext(bundle, contextId, service);
+        }
+    }
+
+    public void remove(HttpContext service)
+    {
+        this.contextManager.removeHttpContext(service);
+    }
+
+    private HttpContext getHttpContext(ServiceReference ref)
+    {
+        Bundle bundle = ref.getBundle();
+        String contextId = getStringProperty(ref, CONTEXT_ID_KEY);
+        return contextId != null ? this.contextManager.getHttpContext(bundle, contextId) : null;
+    }
+
+    public void add(Filter service, ServiceReference ref)
+    {
+        int ranking = getIntProperty(ref, Constants.SERVICE_RANKING, 0);
+        String pattern = getStringProperty(ref, PATTERN_KEY);
+
+        if (pattern == null) {
+            return;
+        }
+
+        FilterMapping mapping = new FilterMapping(getHttpContext(ref), service, pattern, ranking);
+        addInitParams(ref, mapping);
+        addMapping(service, mapping);
+    }
+
+    public void remove(Filter service)
+    {
+        removeMapping(service);
+    }
+
+    public void add(Servlet service, ServiceReference ref)
+    {
+        String alias = getStringProperty(ref, ALIAS_KEY);
+        if (alias == null) {
+            return;
+        }
+
+        ServletMapping mapping = new ServletMapping(getHttpContext(ref), service, alias); 
+        addInitParams(ref, mapping);
+        addMapping(service, mapping);
+    }
+
+    public void remove(Servlet service)
+    {
+        removeMapping(service);
+    }
+
+    public synchronized void setHttpService(HttpService service)
+    {
+        this.httpService = service;
+        if (this.httpService instanceof ExtHttpService) {
+            SystemLogger.get().info("Detected extended HttpService. Filters enabled.");
+        } else {
+            SystemLogger.get().info("Standard HttpService. Filters disabled.");
+        }
+
+        registerAll();
+    }
+
+    public synchronized void unsetHttpService()
+    {
+        unregisterAll();
+        this.httpService = null;
+    }
+
+    public synchronized void unregisterAll()
+    {
+        if (this.httpService != null) {
+            for (AbstractMapping mapping : this.mapping.values()) {
+                mapping.unregister(this.httpService);
+            }
+        }
+    }
+
+    private synchronized void registerAll()
+    {
+        if (this.httpService != null) {
+            for (AbstractMapping mapping : this.mapping.values()) {
+                mapping.register(this.httpService);
+            }
+        }
+    }
+
+    private synchronized void addMapping(Object key, AbstractMapping mapping)
+    {
+        this.mapping.put(key, mapping);
+        if (this.httpService != null) {
+            mapping.register(this.httpService);
+        }
+    }
+
+    private synchronized void removeMapping(Object key)
+    {
+        AbstractMapping mapping = this.mapping.remove(key);
+        if ((mapping != null) && (this.httpService != null)) {
+            mapping.unregister(this.httpService);
+        }
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
new file mode 100644
index 0000000..bcccc0a
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.manager;
+
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.HttpContext;
+import org.apache.felix.http.api.ExtHttpService;
+import org.apache.felix.http.whiteboard.internal.util.SystemLogger;
+import javax.servlet.Filter;
+
+public final class FilterMapping
+    extends AbstractMapping
+{
+    private final Filter filter;
+    private final int ranking;
+    private final String pattern;
+
+    public FilterMapping(HttpContext context, Filter filter, String pattern, int ranking)
+    {
+        super(context);
+        this.filter = filter;
+        this.pattern = pattern;
+        this.ranking = ranking;
+    }
+
+    public void register(HttpService httpService)
+    {    
+        if (httpService instanceof ExtHttpService) {
+            register((ExtHttpService)httpService);
+        }
+    }
+
+    private void register(ExtHttpService httpService)
+    {
+        try {
+            httpService.registerFilter(this.filter, this.pattern, getInitParams(), ranking, getContext());
+        } catch (Exception e) {
+            SystemLogger.get().error("Failed to register filter", e);
+        }
+    }
+
+    public void unregister(HttpService httpService)
+    {
+        if (httpService instanceof ExtHttpService) {
+            unregister((ExtHttpService)httpService);
+        }
+    }
+
+    private void unregister(ExtHttpService httpService)
+    {
+        httpService.unregisterFilter(this.filter);
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
new file mode 100644
index 0000000..d44056e
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.manager;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+import org.apache.felix.http.whiteboard.internal.util.SystemLogger;
+import java.util.HashMap;
+
+public final class HttpContextManager
+{
+    private final HashMap<String, HttpContext> idMap;
+    private final HashMap<HttpContext, String> contextMap;
+
+    public HttpContextManager()
+    {
+        this.idMap = new HashMap<String, HttpContext>();
+        this.contextMap = new HashMap<HttpContext, String>();
+    }
+
+    private String createId(Bundle bundle, String contextId)
+    {
+        return bundle.getBundleId() + "-" + contextId;
+    }
+
+    public synchronized HttpContext getHttpContext(Bundle bundle, String contextId)
+    {
+        String id = createId(bundle, contextId);
+        HttpContext context = this.idMap.get(id);
+
+        if (context == null) {
+            context = new DefaultHttpContext(bundle);
+            this.idMap.put(id, context);
+            this.contextMap.put(context, id);
+            SystemLogger.get().debug("Added context with id [" + contextId + "]");
+        } else {
+            SystemLogger.get().debug("Reusing context with id [" + contextId + "]");            
+        }
+
+        return context;
+    }
+
+    public synchronized void removeHttpContext(HttpContext context)
+    {
+        String id = this.contextMap.remove(context);
+        if (id != null) {
+            this.idMap.remove(id);
+        }
+    }
+
+    public synchronized void addHttpContext(Bundle bundle, String contextId, HttpContext context)
+    {
+        String id = createId(bundle, contextId);
+        this.idMap.put(id, context);
+        this.contextMap.put(context, id);
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
new file mode 100644
index 0000000..408c58d
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.manager;
+
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.HttpContext;
+import org.apache.felix.http.whiteboard.internal.util.SystemLogger;
+import javax.servlet.Servlet;
+
+public final class ServletMapping
+    extends AbstractMapping
+{
+    private final Servlet servlet;
+    private final String alias;
+
+    public ServletMapping(HttpContext context, Servlet servlet, String alias)
+    {
+        super(context);
+        this.servlet = servlet;
+        this.alias = alias;
+    }
+
+    public void register(HttpService httpService)
+    {
+        try {
+            httpService.registerServlet(this.alias, this.servlet, getInitParams(), getContext());
+        } catch (Exception e) {
+            SystemLogger.get().error("Failed to register servlet", e);
+        }
+    }
+
+    public void unregister(HttpService httpService)
+    {
+        httpService.unregister(this.alias);
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java
new file mode 100644
index 0000000..c2590bf
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.tracker;
+
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public abstract class AbstractTracker<T>
+    extends ServiceTracker
+{
+    public AbstractTracker(BundleContext context, Class clz)
+    {
+        super(context, clz.getName(), null);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final Object addingService(ServiceReference ref)
+    {
+        T service = (T)super.addingService(ref);
+        added(service, ref);
+        return service;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final void modifiedService(ServiceReference ref, Object service)
+    {
+        super.modifiedService(ref, service);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final void removedService(ServiceReference ref, Object service)
+    {
+        super.removedService(ref, service);
+        removed((T)service);
+    }
+
+    protected abstract void modified(T service, ServiceReference ref);
+
+    protected abstract void added(T service, ServiceReference ref);
+
+    protected abstract void removed(T service);
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java
new file mode 100644
index 0000000..a2640dd
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.tracker;
+
+import javax.servlet.Filter;
+import org.apache.felix.http.whiteboard.internal.manager.ExtenderManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public final class FilterTracker
+    extends AbstractTracker<Filter>
+{
+    private final ExtenderManager manager;
+
+    public FilterTracker(BundleContext context, ExtenderManager manager)
+    {
+        super(context, Filter.class);
+        this.manager = manager;
+    }
+
+    protected void added(Filter service, ServiceReference ref)
+    {
+        this.manager.add(service, ref);
+    }
+
+    protected void modified(Filter service, ServiceReference ref)
+    {
+        removed(service);
+        added(service, ref);
+    }
+
+    protected void removed(Filter service)
+    {
+        this.manager.remove(service);
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java
new file mode 100644
index 0000000..60af219
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.tracker;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+import org.apache.felix.http.whiteboard.internal.manager.ExtenderManager;
+
+public final class HttpContextTracker
+    extends AbstractTracker<HttpContext>
+{
+    private final ExtenderManager manager;
+    
+    public HttpContextTracker(BundleContext context, ExtenderManager manager)
+    {
+        super(context, HttpContext.class);
+        this.manager = manager;
+    }
+
+    protected void added(HttpContext service, ServiceReference ref)
+    {
+        this.manager.add(service, ref);
+    }
+
+    protected void modified(HttpContext service, ServiceReference ref)
+    {
+        removed(service);
+        added(service, ref);
+    }
+    
+    protected void removed(HttpContext service)
+    {
+        this.manager.remove(service);
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java
new file mode 100644
index 0000000..af61564
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.tracker;
+
+import org.apache.felix.http.whiteboard.internal.manager.ExtenderManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpService;
+
+public final class HttpServiceTracker
+    extends AbstractTracker<HttpService>
+{
+    private final ExtenderManager manager;
+
+    public HttpServiceTracker(BundleContext context, ExtenderManager manager)
+    {
+        super(context, HttpService.class);
+        this.manager = manager;
+    }
+
+    protected void added(HttpService service, ServiceReference ref)
+    {
+        this.manager.setHttpService(service);
+    }
+
+    protected void modified(HttpService service, ServiceReference ref)
+    {
+        // Do nothing
+    }
+
+    protected void removed(HttpService service)
+    {
+        this.manager.unsetHttpService();
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java
new file mode 100644
index 0000000..2dcae99
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.tracker;
+
+import org.apache.felix.http.whiteboard.internal.manager.ExtenderManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import javax.servlet.Servlet;
+
+public final class ServletTracker
+    extends AbstractTracker<Servlet>
+{
+    private final ExtenderManager manager;
+
+    public ServletTracker(BundleContext context, ExtenderManager manager)
+    {
+        super(context, Servlet.class);
+        this.manager = manager;
+    }
+
+    protected void added(Servlet service, ServiceReference ref)
+    {
+        this.manager.add(service, ref);
+    }
+
+    protected void modified(Servlet service, ServiceReference ref)
+    {
+        removed(service);
+        added(service, ref);
+    }
+    
+    protected void removed(Servlet service)
+    {
+        this.manager.remove(service);
+    }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/util/SystemLogger.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/util/SystemLogger.java
new file mode 100644
index 0000000..df68069
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/util/SystemLogger.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.whiteboard.internal.util;
+
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public final class SystemLogger
+{
+    private final static SystemLogger INSTANCE =
+        new SystemLogger();
+    
+    private ServiceTracker tracker;
+
+    private SystemLogger()
+    {
+    }
+
+    public void open(BundleContext context)
+    {
+        this.tracker = new ServiceTracker(context, LogService.class.getName(), null);
+        this.tracker.open();
+    }
+
+    public void close()
+    {
+        this.tracker.close();
+    }
+
+    public void debug(String message)
+    {
+        log(LogService.LOG_DEBUG, message, null);
+    }
+
+    public void info(String message)
+    {
+        log(LogService.LOG_INFO, message, null);
+    }
+
+    public void warning(String message, Throwable cause)
+    {
+        log(LogService.LOG_WARNING, message, cause);
+    }
+
+    public void error(String message, Throwable cause)
+    {
+        log(LogService.LOG_ERROR, message, cause);
+    }
+
+    private void log(int level, String message, Throwable cause)
+    {
+        LogService log = (LogService)this.tracker.getService();
+        if (log != null) {
+            log.log(level, message, cause);
+        } else {
+            System.out.println(message);
+            if (cause != null) {
+                cause.printStackTrace(System.out);
+            }
+        }
+    }
+
+    public static SystemLogger get()
+    {
+        return INSTANCE;
+    }
+}
\ No newline at end of file