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/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");
+    }
+}