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