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/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