FELIX-4060 : Implement HTTP Service Update (RFC-189)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1655654 13f79535-47bb-0310-9956-ffa450edef68
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
index 793bea8..a3ee0d5 100644
--- 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
@@ -16,7 +16,9 @@
*/
package org.apache.felix.http.base.internal.handler;
-import static javax.servlet.http.HttpServletResponse.*;
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+
import java.io.IOException;
import java.util.regex.Pattern;
@@ -27,6 +29,7 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
public final class FilterHandler extends AbstractHandler implements Comparable<FilterHandler>
{
@@ -42,6 +45,16 @@
this.regex = Pattern.compile(pattern);
}
+ public FilterHandler(ExtServletContext context, Filter filter, FilterInfo filterInfo)
+ {
+ // TODO
+ super(context, filterInfo.name);
+ this.filter = filter;
+ this.ranking = filterInfo.ranking;
+ this.regex = Pattern.compile(filterInfo.regexs[0]);
+ }
+
+ @Override
public int compareTo(FilterHandler other)
{
if (other.ranking == this.ranking)
@@ -52,6 +65,7 @@
return (other.ranking > this.ranking) ? 1 : -1;
}
+ @Override
public void destroy()
{
this.filter.destroy();
@@ -85,6 +99,7 @@
}
}
+ @Override
public void init() throws ServletException
{
this.filter.init(new FilterConfigImpl(getName(), getContext(), getInitParams()));
@@ -109,7 +124,7 @@
}
else
{
- // FELIX-3988: If the response is not yet committed and still has the default
+ // FELIX-3988: If the response is not yet committed and still has the default
// status, we're going to override this and send an error instead.
if (!res.isCommitted() && (res.getStatus() == SC_OK || res.getStatus() == 0))
{
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
index 3ba93d7..feb1a1b 100644
--- 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
@@ -121,6 +121,11 @@
return aliasMap.get(alias);
}
+ public void addErrorServlet(String errorPage, ServletHandler handler) throws ServletException
+ {
+ // TODO
+ }
+
public void removeAll()
{
for (Iterator<ServletHandler> it = servletMap.values().iterator(); it.hasNext(); )
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java
index d1d0aae..127e539 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpServicePlugin.java
@@ -19,20 +19,22 @@
package org.apache.felix.http.base.internal.handler;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceRegistration;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Properties;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
@SuppressWarnings("serial")
public class HttpServicePlugin extends HttpServlet
@@ -51,7 +53,7 @@
public void register()
{
- Properties props = new Properties();
+ final Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
props.put(Constants.SERVICE_DESCRIPTION, "HTTP Service Web Console Plugin");
props.put("felix.webconsole.label", "httpservice");
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
index f653930..f990685 100644
--- 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
@@ -43,6 +43,7 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
/**
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
@@ -73,6 +74,7 @@
this.named = false;
}
+ @Override
public void forward(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
if (res.isCommitted())
@@ -98,6 +100,7 @@
}
}
+ @Override
public void include(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
// Since we're already created this RequestDispatcher for *this* servlet handler, we do not need to
@@ -241,6 +244,15 @@
this.servlet = servlet;
}
+ public ServletHandler(ExtServletContext context, Servlet servlet, ServletInfo servletInfo)
+ {
+ // TODO
+ super(context, servletInfo.name);
+ this.servlet = servlet;
+ this.alias = servletInfo.patterns[0];
+ }
+
+ @Override
public int compareTo(ServletHandler other)
{
return other.alias.length() - this.alias.length();
@@ -256,6 +268,7 @@
return new RequestDispatcherImpl(path, pathInContext, query);
}
+ @Override
public void destroy()
{
this.servlet.destroy();
@@ -300,6 +313,7 @@
return matches;
}
+ @Override
public void init() throws ServletException
{
this.servlet.init(new ServletConfigImpl(getName(), getContext(), getInitParams()));
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java
new file mode 100644
index 0000000..73571d5
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java
@@ -0,0 +1,104 @@
+/*
+ * 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.runtime;
+
+import java.util.Map;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+
+import org.osgi.dto.DTO;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+
+import aQute.bnd.annotation.ConsumerType;
+
+/**
+ * Provides registration information for a {@link Filter}, and is used to programmatically register {@link Filter}s.
+ * <p>
+ * This class only provides information used at registration time, and as such differs slightly from {@link DTO}s like, {@link FilterDTO}.
+ * </p>
+ *
+ * TODO - we should move this to the same place as {@link ExtHttpServiceRuntime}.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@ConsumerType
+public final class FilterInfo
+{
+ /**
+ * The name of the servlet.
+ */
+ public String name;
+
+ /**
+ * The request mappings for the servlet.
+ * <p>
+ * The specified patterns are used to determine whether a request is mapped to the servlet filter.<br>
+ * Note that these patterns should conform to the Servlet specification.
+ * </p>
+ */
+ public String[] patterns;
+
+ /**
+ * The servlet names for the servlet filter.
+ * <p>
+ * The specified names are used to determine the servlets whose requests are mapped to the servlet filter.
+ * </p>
+ */
+ public String[] servletNames;
+
+ /**
+ * The request mappings for the servlet filter.
+ * <p>
+ * The specified regular expressions are used to determine whether a request is mapped to the servlet filter.<br>
+ * These regular expressions are a convenience extension allowing one to specify filters that match paths that are difficult to match with plain Servlet patterns alone.
+ * </p>
+ */
+ public String[] regexs;
+
+ /**
+ * Specifies whether the servlet filter supports asynchronous processing.
+ */
+ public boolean asyncSupported = false;
+
+ /**
+ * Specifies the ranking order in which this filter should be called. Higher rankings are called first.
+ */
+ public int ranking = 0;
+
+ /**
+ * The dispatcher associations for the servlet filter.
+ * <p>
+ * The specified names are used to determine in what occasions the servlet filter is called.
+ * See {@link DispatcherType} and Servlet 3.0 specification, section 6.2.5.
+ * </p>
+ */
+ public DispatcherType[] dispatcher = { DispatcherType.REQUEST };
+
+ /**
+ * The filter initialization parameters as provided during registration of the filter.
+ */
+ public Map<String, String> initParams;
+
+ /**
+ * The {@link HttpContext} for the servlet.
+ */
+ public HttpContext context;
+
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java
new file mode 100644
index 0000000..dddb544
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.http.base.internal.runtime;
+
+import java.util.Map;
+
+import javax.servlet.Servlet;
+
+import org.osgi.dto.DTO;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+
+import aQute.bnd.annotation.ConsumerType;
+
+/**
+ * Provides registration information for a {@link Servlet}, and is used to programmatically register {@link Servlet}s.
+ * <p>
+ * This class only provides information used at registration time, and as such differs slightly from {@link DTO}s like, {@link ServletDTO}.
+ * </p>
+ *
+ * TODO - we should move this to the same place as {@link ExtHttpServiceRuntime}.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@ConsumerType
+public final class ServletInfo
+{
+ /**
+ * The name of the servlet.
+ */
+ public String name;
+
+ /**
+ * The request mappings for the servlet.
+ * <p>
+ * The specified patterns are used to determine whether a request is mapped to the servlet.
+ * </p>
+ */
+ public String[] patterns;
+
+ /**
+ * The error pages and/or codes.
+ */
+ public String[] errorPage;
+
+ /**
+ * Specifies whether the servlet supports asynchronous processing.
+ */
+ public boolean asyncSupported = false;
+
+ /**
+ * The servlet initialization parameters as provided during registration of the servlet.
+ */
+ public Map<String, String> initParams;
+
+ /**
+ * The {@link HttpContext} for the servlet.
+ */
+ public HttpContext context;
+
+}
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
index ce4cdc0..f591b1c 100644
--- 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
@@ -17,7 +17,10 @@
package org.apache.felix.http.base.internal.service;
import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.Servlet;
@@ -32,6 +35,8 @@
import org.apache.felix.http.base.internal.handler.HandlerRegistry;
import org.apache.felix.http.base.internal.handler.ServletHandler;
import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
import org.osgi.framework.Bundle;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.NamespaceException;
@@ -53,16 +58,85 @@
this.contextManager = new ServletContextManager(this.bundle, context, servletAttributeListener, sharedContextAttributes);
}
- private ExtServletContext getServletContext(HttpContext context)
+ static Map<String, String> convertToMap(Dictionary dict)
{
- if (context == null)
+ Map<String, String> result = new HashMap<String, String>();
+ if (dict != null)
{
- context = createDefaultHttpContext();
+ Enumeration keyEnum = dict.keys();
+ while (keyEnum.hasMoreElements())
+ {
+ String key = String.valueOf(keyEnum.nextElement());
+ Object value = dict.get(key);
+ result.put(key, value == null ? null : String.valueOf(value));
+ }
}
-
- return this.contextManager.getServletContext(context);
+ return result;
}
+ static <T> boolean isEmpty(T[] array)
+ {
+ return array == null || array.length < 1;
+ }
+
+ static boolean isEmpty(String str)
+ {
+ return str == null || "".equals(str.trim());
+ }
+
+ @Override
+ public HttpContext createDefaultHttpContext()
+ {
+ return new DefaultHttpContext(this.bundle);
+ }
+
+ public void registerFilter(Filter filter, FilterInfo filterInfo) throws ServletException
+ {
+ if (filter == null)
+ {
+ throw new IllegalArgumentException("Filter cannot be null!");
+ }
+ if (filterInfo == null)
+ {
+ throw new IllegalArgumentException("FilterInfo cannot be null!");
+ }
+ if (isEmpty(filterInfo.patterns) && isEmpty(filterInfo.regexs) && isEmpty(filterInfo.servletNames))
+ {
+ throw new IllegalArgumentException("FilterInfo must have at least one pattern or regex, or provide at least one servlet name!");
+ }
+ if (isEmpty(filterInfo.name))
+ {
+ filterInfo.name = filter.getClass().getName();
+ }
+
+ ExtServletContext servletContext = getServletContext(filterInfo.context);
+
+ try
+ {
+ FilterHandler handler = new FilterHandler(servletContext, filter, filterInfo);
+
+ synchronized (this)
+ {
+ if (this.localFilters.add(filter))
+ {
+ this.handlerRegistry.addFilter(handler);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (e instanceof ServletException)
+ {
+ throw (ServletException) e;
+ }
+ else
+ {
+ throw new ServletException("Failed to register filter " + filterInfo.name, e);
+ }
+ }
+ }
+
+ @Override
public void registerFilter(Filter filter, String pattern, Dictionary initParams, int ranking, HttpContext context) throws ServletException
{
if (filter == null)
@@ -76,14 +150,80 @@
this.localFilters.add(filter);
}
- public void unregisterFilter(Filter filter)
+ @Override
+ public void registerResources(String alias, String name, HttpContext context) throws NamespaceException
{
- unregisterFilter(filter, true);
+ 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.error("Failed to register resources", e);
+ }
}
- public void unregisterServlet(Servlet servlet)
+ public void registerServlet(Servlet servlet, ServletInfo servletInfo) throws ServletException, NamespaceException
{
- unregisterServlet(servlet, true);
+ if (servlet == null)
+ {
+ throw new IllegalArgumentException("Servlet cannot be null!");
+ }
+ if (servletInfo == null)
+ {
+ throw new IllegalArgumentException("ServletInfo cannot be null!");
+ }
+ if (isEmpty(servletInfo.patterns) && isEmpty(servletInfo.errorPage))
+ {
+ throw new IllegalArgumentException("ServletInfo must at least have one pattern or error page!");
+ }
+ if (isEmpty(servletInfo.name))
+ {
+ servletInfo.name = servlet.getClass().getName();
+ }
+
+ ExtServletContext servletContext = getServletContext(servletInfo.context);
+
+ try
+ {
+ ServletHandler handler = new ServletHandler(servletContext, servlet, servletInfo);
+
+ synchronized (this)
+ {
+ if (this.localServlets.add(servlet))
+ {
+ this.handlerRegistry.addServlet(handler);
+ if (servletInfo.errorPage != null && servletInfo.errorPage.length != 0)
+ {
+ for (String errorPage : servletInfo.errorPage)
+ {
+ this.handlerRegistry.addErrorServlet(errorPage, handler);
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (e instanceof ServletException)
+ {
+ throw (ServletException) e;
+ }
+ else if (e instanceof NamespaceException)
+ {
+ throw (NamespaceException) e;
+ }
+ else
+ {
+ throw new ServletException("Failed to register servlet " + servletInfo.name, e);
+ }
+ }
}
public void registerServlet(String alias, Servlet servlet, Dictionary initParams, HttpContext context) throws ServletException, NamespaceException
@@ -103,24 +243,6 @@
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.error("Failed to register resources", e);
- }
- }
-
public void unregister(String alias)
{
final Servlet servlet = this.handlerRegistry.getServletByAlias(alias);
@@ -139,11 +261,6 @@
unregisterServlet(servlet);
}
- public HttpContext createDefaultHttpContext()
- {
- return new DefaultHttpContext(this.bundle);
- }
-
public void unregisterAll()
{
HashSet<Servlet> servlets = new HashSet<Servlet>(this.localServlets);
@@ -159,6 +276,43 @@
}
}
+ @Override
+ public void unregisterFilter(Filter filter)
+ {
+ unregisterFilter(filter, true);
+ }
+
+ @Override
+ public void unregisterServlet(Servlet servlet)
+ {
+ unregisterServlet(servlet, true);
+ }
+
+ private ExtServletContext getServletContext(HttpContext context)
+ {
+ if (context == null)
+ {
+ context = createDefaultHttpContext();
+ }
+
+ return this.contextManager.getServletContext(context);
+ }
+
+ private boolean isNameValid(String name)
+ {
+ if (name == null)
+ {
+ return false;
+ }
+
+ if (!name.equals("/") && name.endsWith("/"))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
private void unregisterFilter(Filter filter, final boolean destroy)
{
if (filter != null)
@@ -176,22 +330,7 @@
this.localServlets.remove(servlet);
}
}
-
- private boolean isNameValid(String name)
- {
- if (name == null)
- {
- return false;
- }
-
- if (!name.equals("/") && name.endsWith("/"))
- {
- return false;
- }
-
- return true;
- }
-
+
private boolean isAliasValid(String alias)
{
if (alias == null)
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceRuntimeImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceRuntimeImpl.java
new file mode 100644
index 0000000..1c54bd2
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceRuntimeImpl.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.service;
+
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+import org.osgi.service.http.runtime.dto.FailedErrorPageDTO;
+import org.osgi.service.http.runtime.dto.FailedFilterDTO;
+import org.osgi.service.http.runtime.dto.FailedServletDTO;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.runtime.dto.RuntimeDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+
+public final class HttpServiceRuntimeImpl implements HttpServiceRuntime
+{
+
+ @Override
+ public RuntimeDTO getRuntimeDTO()
+ {
+ // create new DTO on every call
+ final RuntimeDTO runtime = new RuntimeDTO();
+
+ return runtime;
+ }
+
+ public void registerServlet(ServletDTO servletDTO)
+ {
+ // TODO
+ }
+
+ public void registerFailedServlet(FailedServletDTO failedServletDTO)
+ {
+ // TODO
+ }
+
+ public void registerErrorPage(ErrorPageDTO errorPageDTO)
+ {
+ // TODO
+ }
+
+ public void registerFailedErrorPage(FailedErrorPageDTO failedErrorPageDTO)
+ {
+ // TODO
+ }
+
+ public void registerFilter(FilterDTO filterDTO)
+ {
+ // TODO
+ }
+
+ public void registerFailedFilter(FailedFilterDTO failedFilterDTO)
+ {
+ // TODO
+ }
+
+ @Override
+ public RequestInfoDTO calculateRequestInfoDTO(String path) {
+ // TODO
+ return null;
+ }
+
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/AbstractMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/AbstractMapping.java
new file mode 100644
index 0000000..52c2c13
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/AbstractMapping.java
@@ -0,0 +1,74 @@
+/*
+ * 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.whiteboard;
+
+import java.util.Hashtable;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+
+abstract class AbstractMapping
+{
+ private final Bundle bundle;
+ private HttpContext context;
+ private final Hashtable<String, String> initParams;
+ private boolean registered;
+
+ protected AbstractMapping(final Bundle bundle)
+ {
+ this.bundle = bundle;
+ this.context = null;
+ this.initParams = new Hashtable<String, String>();
+ this.registered = false;
+ }
+
+ public Bundle getBundle()
+ {
+ return bundle;
+ }
+
+ public void setContext(HttpContext context)
+ {
+ this.context = context;
+ }
+
+ public final HttpContext getContext()
+ {
+ return this.context;
+ }
+
+ public final Hashtable<String, String> getInitParams()
+ {
+ return this.initParams;
+ }
+
+ boolean isRegistered()
+ {
+ return registered;
+ }
+
+ void setRegistered(boolean registered)
+ {
+ this.registered = registered;
+ }
+
+ public abstract void register(HttpService httpService);
+
+ public abstract void unregister(HttpService httpService);
+
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/DefaultHttpContext.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/DefaultHttpContext.java
new file mode 100644
index 0000000..6853a16
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/DefaultHttpContext.java
@@ -0,0 +1,59 @@
+/*
+ * 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.whiteboard;
+
+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;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getClass().getSimpleName() + " (" + Integer.toHexString(System.identityHashCode(this)) + ")";
+ }
+}
\ No newline at end of file
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
new file mode 100644
index 0000000..ce7fce3
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
@@ -0,0 +1,453 @@
+/*
+ * 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.whiteboard;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.whiteboard.HttpContextManager.HttpContextHolder;
+import org.apache.felix.http.base.internal.whiteboard.tracker.FilterTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextHelperTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ServletTracker;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+import org.osgi.util.tracker.ServiceTracker;
+
+@SuppressWarnings({ "deprecation" })
+public final class ExtenderManager
+{
+ static final String TYPE_FILTER = "f";
+ static final String TYPE_SERVLET = "s";
+ static final String TYPE_RESOURCE = "r";
+
+ /**
+ * Properties starting with this prefix are passed as servlet init parameters to the
+ * {@code init()} method of the servlet.
+ */
+ public static final String SERVLET_INIT_PREFIX = "servlet.init.";
+
+ /**
+ * Properties starting with this prefix are passed as filter
+ * init parameters to the {@code init()} method of the filter.
+ */
+ public static final String FILTER_INIT_PREFIX = "filter.init.";
+
+ private final Map<String, AbstractMapping> mapping;
+ private final HttpContextManager contextManager;
+
+ private final HttpService httpService;
+
+ private final ArrayList<ServiceTracker> trackers = new ArrayList<ServiceTracker>();
+
+ public ExtenderManager(final HttpService httpService, final BundleContext bundleContext)
+ {
+ this.mapping = new HashMap<String, AbstractMapping>();
+ this.contextManager = new HttpContextManager();
+ this.httpService = httpService;
+ addTracker(new FilterTracker(bundleContext, this));
+ addTracker(new ServletTracker(bundleContext, this));
+ addTracker(new ServletContextHelperTracker(bundleContext, this));
+ }
+
+ public void close()
+ {
+ for(final ServiceTracker t : this.trackers)
+ {
+ t.close();
+ }
+ this.trackers.clear();
+ this.unregisterAll();
+ }
+
+ private void addTracker(ServiceTracker tracker)
+ {
+ this.trackers.add(tracker);
+ tracker.open();
+ }
+
+ static boolean isEmpty(final String value)
+ {
+ return value == null || value.length() == 0;
+ }
+
+ static boolean isEmpty(final String[] value)
+ {
+ return value == null || value.length == 0;
+ }
+
+ private String getStringProperty(ServiceReference ref, String key)
+ {
+ Object value = ref.getProperty(key);
+ return (value instanceof String) ? (String) value : null;
+ }
+
+ private String[] getStringArrayProperty(ServiceReference ref, String key)
+ {
+ Object value = ref.getProperty(key);
+
+ if (value instanceof String)
+ {
+ return new String[] { (String) value };
+ }
+ else if (value instanceof String[])
+ {
+ return (String[]) value;
+ }
+ else if (value instanceof Collection<?>)
+ {
+ Collection<?> collectionValues = (Collection<?>) value;
+ String[] values = new String[collectionValues.size()];
+
+ int i = 0;
+ for (Object current : collectionValues)
+ {
+ values[i++] = current != null ? String.valueOf(current) : null;
+ }
+
+ return values;
+ }
+
+ return null;
+ }
+
+ private boolean getBooleanProperty(ServiceReference ref, String key)
+ {
+ Object value = ref.getProperty(key);
+ if (value instanceof String)
+ {
+ return Boolean.valueOf((String) value);
+ }
+ else if (value instanceof Boolean)
+ {
+ return ((Boolean) value).booleanValue();
+ }
+ return false;
+ }
+
+ private int getIntProperty(ServiceReference ref, String key, int defValue)
+ {
+ Object value = ref.getProperty(key);
+ if (value == null)
+ {
+ return defValue;
+ }
+
+ try
+ {
+ return Integer.parseInt(value.toString());
+ }
+ catch (Exception e)
+ {
+ return defValue;
+ }
+ }
+
+ private void addInitParams(ServiceReference ref, AbstractMapping mapping)
+ {
+ for (String key : ref.getPropertyKeys())
+ {
+ String prefixKey = null;
+
+ if (mapping instanceof FilterMapping && key.startsWith(FILTER_INIT_PREFIX))
+ {
+ prefixKey = FILTER_INIT_PREFIX;
+ }
+ else if (mapping instanceof ServletMapping && key.startsWith(SERVLET_INIT_PREFIX))
+ {
+ prefixKey = SERVLET_INIT_PREFIX;
+ }
+
+ if (prefixKey != null)
+ {
+ String paramKey = key.substring(prefixKey.length());
+ String paramValue = getStringProperty(ref, key);
+
+ if (paramValue != null)
+ {
+ mapping.getInitParams().put(paramKey, paramValue);
+ }
+ }
+ }
+ }
+
+ public void add(ServletContextHelper service, ServiceReference ref)
+ {
+ String name = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME);
+ String path = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH);
+
+ // TODO - check if name and path are valid values
+ if (!isEmpty(name) && !isEmpty(path) )
+ {
+ Collection<AbstractMapping> mappings = this.contextManager.addContextHelper(ref.getBundle(), name, path, service);
+ for (AbstractMapping mapping : mappings)
+ {
+ registerMapping(mapping);
+ }
+ }
+ else
+ {
+ SystemLogger.debug("Ignoring ServletContextHelper Service " + ref + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + " is missing or empty");
+ }
+ }
+
+ public void addResource(final ServiceReference ref)
+ {
+ final String[] pattern = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN);
+ final String prefix = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX);
+
+ if (!isEmpty(pattern))
+ {
+ if ( !isEmpty(prefix))
+ {
+ for(final String p : pattern)
+ {
+ // TODO : check if p is empty - and then log?
+ final ResourceDTO resourceDTO = new ResourceDTO();
+ resourceDTO.patterns = new String[] {p};
+ resourceDTO.prefix = prefix;
+ final ResourceMapping mapping = new ResourceMapping(ref.getBundle(), resourceDTO);
+ this.addMapping(TYPE_RESOURCE, ref, mapping);
+ }
+ }
+ else
+ {
+ SystemLogger.debug("Ignoring Resource Service " + ref + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX + " is missing or empty");
+ }
+ }
+ else
+ {
+ SystemLogger.debug("Ignoring Resource Service " + ref + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN + " is missing or empty");
+ }
+ }
+
+ public void removeResource(final ServiceReference ref)
+ {
+ this.removeMapping(TYPE_RESOURCE, ref);
+ }
+
+ public void remove(ServletContextHelper service)
+ {
+ Collection<AbstractMapping> mappings = this.contextManager.removeContextHelper(service);
+ if (mappings != null)
+ {
+ for (AbstractMapping mapping : mappings)
+ {
+ unregisterMapping(mapping);
+ }
+ }
+ }
+
+ private HttpContext getHttpContext(AbstractMapping mapping, ServiceReference ref)
+ {
+ Bundle bundle = ref.getBundle();
+ String contextName = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT);
+ if (!isEmpty(contextName))
+ {
+ return this.contextManager.getHttpContext(bundle, contextName, mapping, true);
+ }
+ return this.contextManager.getHttpContext(bundle, null, mapping);
+ }
+
+ private void ungetHttpContext(AbstractMapping mapping, ServiceReference ref)
+ {
+ Bundle bundle = ref.getBundle();
+ String contextName = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT);
+ if (!isEmpty(contextName))
+ {
+ this.contextManager.ungetHttpContext(bundle, contextName, mapping, true);
+ return;
+ }
+ this.contextManager.ungetHttpContext(bundle, null, mapping);
+ }
+
+ public void add(final Filter service, final ServiceReference ref)
+ {
+ FilterInfo filterInfo = new FilterInfo();
+ filterInfo.name = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME);
+ if ( filterInfo.name == null || filterInfo.name.isEmpty() )
+ {
+ filterInfo.name = service.getClass().getName();
+ }
+ filterInfo.asyncSupported = getBooleanProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED);
+ filterInfo.servletNames = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET);
+ filterInfo.patterns = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN);
+ filterInfo.ranking = getIntProperty(ref, Constants.SERVICE_RANKING, 0);
+
+ String[] dispatcherNames = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER);
+ if (dispatcherNames != null && dispatcherNames.length > 0)
+ {
+ DispatcherType[] dispatchers = new DispatcherType[dispatcherNames.length];
+ for (int i = 0; i < dispatchers.length; i++)
+ {
+ dispatchers[i] = DispatcherType.valueOf(dispatcherNames[i].toUpperCase());
+ }
+ filterInfo.dispatcher = dispatchers;
+ }
+
+ if (isEmpty(filterInfo.patterns))
+ {
+ SystemLogger.debug("Ignoring Filter Service " + ref + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN +
+ " is missing or empty");
+ return;
+ }
+
+ FilterMapping mapping = new FilterMapping(ref.getBundle(), service, filterInfo);
+ filterInfo.context = getHttpContext(mapping, ref); // XXX
+ addInitParams(ref, mapping);
+ addMapping(TYPE_FILTER, ref, mapping);
+ }
+
+ public void add(Servlet service, ServiceReference ref)
+ {
+ ServletInfo servletInfo = new ServletInfo();
+ servletInfo.name = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME);
+ if ( servletInfo.name == null || servletInfo.name.isEmpty() )
+ {
+ servletInfo.name = service.getClass().getName();
+ }
+ servletInfo.errorPage = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE);
+ servletInfo.patterns = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN);
+ servletInfo.asyncSupported = getBooleanProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED);
+
+ if (isEmpty(servletInfo.patterns))
+ {
+ SystemLogger.debug("Ignoring Servlet Service " + ref + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN +
+ "is missing or empty");
+ return;
+ }
+
+ final ServletMapping mapping = new ServletMapping(ref.getBundle(), service, servletInfo);
+ servletInfo.context = getHttpContext(mapping, ref); // XXX
+ addInitParams(ref, mapping);
+ addMapping(TYPE_SERVLET, ref, mapping);
+ }
+
+ public void removeFilter(ServiceReference ref)
+ {
+ removeMapping(TYPE_FILTER, ref);
+ }
+
+ public void removeServlet(ServiceReference ref)
+ {
+ removeMapping(TYPE_SERVLET, ref);
+ }
+
+ private synchronized void unregisterAll()
+ {
+ AbstractMapping[] mappings = null;
+ HttpService service;
+ synchronized (this)
+ {
+ service = this.httpService;
+ if (service != null)
+ {
+ Collection<AbstractMapping> values = this.mapping.values();
+ mappings = values.toArray(new AbstractMapping[values.size()]);
+ }
+ }
+ if (mappings != null)
+ {
+ for (AbstractMapping mapping : mappings)
+ {
+ mapping.unregister(service);
+ }
+ }
+ }
+
+ private synchronized void addMapping(final String servType, ServiceReference ref, AbstractMapping mapping)
+ {
+ this.mapping.put(ref.getProperty(Constants.SERVICE_ID).toString().concat(servType), mapping);
+ this.registerMapping(mapping);
+ }
+
+ private synchronized void removeMapping(final String servType, ServiceReference ref)
+ {
+ AbstractMapping mapping = this.mapping.remove(ref.getProperty(Constants.SERVICE_ID).toString().concat(servType));
+ if (mapping != null)
+ {
+ ungetHttpContext(mapping, ref);
+ unregisterMapping(mapping);
+ }
+ }
+
+ private void registerMapping(AbstractMapping mapping)
+ {
+ HttpService httpService = this.httpService;
+ if (httpService != null)
+ {
+ mapping.register(httpService);
+ }
+ }
+
+ private void unregisterMapping(AbstractMapping mapping)
+ {
+ HttpService httpService = this.httpService;
+ if (httpService != null)
+ {
+ mapping.unregister(httpService);
+ }
+ }
+
+ /**
+ * Returns
+ * {@link org.apache.felix.http.base.internal.whiteboard.whiteboard.internal.manager.HttpContextManager.HttpContextHolder}
+ * instances of HttpContext services.
+ *
+ * @return
+ */
+ Map<String, HttpContextHolder> getHttpContexts()
+ {
+ return this.contextManager.getHttpContexts();
+ }
+
+ /**
+ * Returns {@link AbstractMapping} instances for which there is no
+ * registered HttpContext as desired by the context ID.
+ */
+ Map<String, Set<AbstractMapping>> getOrphanMappings()
+ {
+ return this.contextManager.getOrphanMappings();
+ }
+
+ /**
+ * Returns mappings indexed by there owning OSGi service.
+ */
+ Map<String, AbstractMapping> getMappings()
+ {
+ synchronized (this)
+ {
+ return new HashMap<String, AbstractMapping>(this.mapping);
+ }
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/FilterMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/FilterMapping.java
new file mode 100644
index 0000000..58819b2
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/FilterMapping.java
@@ -0,0 +1,110 @@
+/*
+ * 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.whiteboard;
+
+import javax.servlet.Filter;
+
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.service.HttpServiceImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpService;
+
+public final class FilterMapping extends AbstractMapping
+{
+ private final Filter filter;
+ private final FilterInfo filterInfo;
+
+ public FilterMapping(Bundle bundle, Filter filter, FilterInfo filterInfo)
+ {
+ super(bundle);
+ this.filter = filter;
+ this.filterInfo = filterInfo;
+ }
+
+ @Override
+ public void register(HttpService httpService)
+ {
+ if (!isRegistered() && (httpService instanceof HttpServiceImpl) && getContext() != null)
+ {
+ register((HttpServiceImpl) httpService);
+ }
+ else
+ {
+ // Warn the user that something strange is going on...
+ SystemLogger.warning("Unable to register filter for " + this.filterInfo.name + ", as no ExtHttpService seems to be present!", null);
+ }
+ }
+
+ @Override
+ public void unregister(HttpService httpService)
+ {
+ if (isRegistered() && (httpService instanceof HttpServiceImpl))
+ {
+ unregister((HttpServiceImpl) httpService);
+ }
+ else
+ {
+ // Warn the user that something strange is going on...
+ SystemLogger.warning("Unable to unregister filter for " + this.filterInfo.name + ", as no ExtHttpService seems to be present!", null);
+ }
+ }
+
+ Filter getFilter()
+ {
+ return filter;
+ }
+
+ private void register(HttpServiceImpl httpService)
+ {
+ if (!isRegistered() && getContext() != null)
+ {
+ try
+ {
+ httpService.registerFilter(this.filter, this.filterInfo);
+ setRegistered(true);
+ }
+ catch (Exception e)
+ {
+ // Warn that something might have gone astray...
+ SystemLogger.warning("Failed to register filter for " + this.filterInfo.name, null);
+ SystemLogger.debug("Failed to register filter for " + this.filterInfo.name + "; details:", e);
+ }
+ }
+ }
+
+ private void unregister(HttpServiceImpl httpService)
+ {
+ if (isRegistered())
+ {
+ try
+ {
+ httpService.unregisterFilter(this.filter);
+ }
+ catch (Exception e)
+ {
+ // Warn that something might have gone astray...
+ SystemLogger.debug("Failed to unregister filter for " + this.filterInfo.name, e);
+ }
+ finally
+ {
+ // Best effort: avoid mappings that are registered which is reality aren't registered...
+ setRegistered(false);
+ }
+ }
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextBridge.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextBridge.java
new file mode 100644
index 0000000..802ddd3
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextBridge.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.whiteboard;
+
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.context.ServletContextHelper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Set;
+
+public class HttpContextBridge extends ServletContextHelper implements HttpContext {
+
+ private final ServletContextHelper delegatee;
+
+ HttpContextBridge(final ServletContextHelper delegatee)
+ {
+ this.delegatee = delegatee;
+ }
+
+ @Override
+ public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ return delegatee.handleSecurity(request, response);
+ }
+
+ @Override
+ public URL getResource(String name)
+ {
+ return delegatee.getResource(name);
+ }
+
+ @Override
+ public String getMimeType(String name)
+ {
+ return delegatee.getMimeType(name);
+ }
+
+ @Override
+ public Set<String> getResourcePaths(String path)
+ {
+ return delegatee.getResourcePaths(path);
+ }
+
+ @Override
+ public String getRealPath(String path)
+ {
+ return delegatee.getRealPath(path);
+ }
+
+ public ServletContextHelper getDelegatee()
+ {
+ return delegatee;
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextManager.java
new file mode 100644
index 0000000..c610284
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextManager.java
@@ -0,0 +1,357 @@
+/*
+ * 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.whiteboard;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.context.ServletContextHelper;
+
+public final class HttpContextManager
+{
+ /**
+ * HttpContextHolders indexed by context ID fully configured
+ * with an HttpContext and optional servlets and filters.
+ * <p>
+ * The context ID either includes the bundle ID as the first part of the
+ * name, such as <i>123-sample.context</i> in the case of non-shared
+ * contexts. IDs of shared contexts are prefixed with the fixed string
+ * <code>shared</code> to not mix them with per-bundle contexts.
+ */
+ private final HashMap<String, HttpContextHolder> idMap;
+
+ /**
+ * Reverse mapping of HttpContext services to the context ID or
+ * name with which they are registered.
+ */
+ private final HashMap<HttpContext, String> contextMap;
+
+ /**
+ * Mapping of registered ServletContextHelper services to the
+ * HttpContext stored in <code>contextMap</code>.
+ */
+ private final HashMap<ServletContextHelper, HttpContext> helperMap;
+
+ /**
+ * Map of servlets and filters registered referring to unregistered
+ * contexts as of yet.
+ */
+ private final HashMap<String, Set<AbstractMapping>> orphanMappings;
+
+ public HttpContextManager()
+ {
+ this.idMap = new HashMap<String, HttpContextHolder>();
+ this.contextMap = new HashMap<HttpContext, String>();
+ this.helperMap = new HashMap<ServletContextHelper, HttpContext>();
+ this.orphanMappings = new HashMap<String, Set<AbstractMapping>>();
+ }
+
+ private static String createId(Bundle bundle, String contextId, boolean isContextHelper)
+ {
+ if (isContextHelper)
+ {
+ return "servletcontexthelper-" + ((contextId == null) ? "" : contextId);
+ }
+ if (bundle != null)
+ {
+ return bundle.getBundleId() + "-" + ((contextId == null) ? "" : contextId);
+ }
+
+ return createId(contextId);
+ }
+
+ private static String createId(String contextId)
+ {
+ return "shared-" + ((contextId == null) ? "" : contextId);
+ }
+
+ private static String getContextId(String id)
+ {
+ final int dash = id.indexOf('-');
+ return (dash < 0) ? id : id.substring(dash + 1);
+ }
+
+ public synchronized HttpContext getHttpContext(Bundle bundle, String contextId, AbstractMapping mapping)
+ {
+ return getHttpContext(bundle, contextId, mapping, false);
+ }
+
+ public synchronized HttpContext getHttpContext(Bundle bundle, String contextId, AbstractMapping mapping,
+ boolean isContextHelper)
+ {
+ // per-bundle context
+ String id = createId(bundle, contextId, isContextHelper);
+ HttpContextHolder holder = this.idMap.get(id);
+
+ // shared context
+ if (holder == null)
+ {
+ id = createId(contextId);
+ holder = this.idMap.get(id);
+ }
+
+ // no context yet, put the mapping on hold
+ if (holder == null)
+ {
+ // care for default context if no context ID
+ if (ExtenderManager.isEmpty(contextId))
+ {
+ addHttpContext(bundle, "", new DefaultHttpContext(bundle));
+ return getHttpContext(bundle, "", mapping);
+ }
+
+ // otherwise context is not here yet
+ Set<AbstractMapping> orphaned = this.orphanMappings.get(contextId);
+ if (orphaned == null)
+ {
+ orphaned = new HashSet<AbstractMapping>();
+ this.orphanMappings.put(contextId, orphaned);
+ }
+ if (contextId != null)
+ {
+ // Only log something when an actual context ID is used. Should solve FELIX-4307...
+ SystemLogger.debug("Holding off mapping with unregistered context with id [" + contextId + "]");
+ }
+ orphaned.add(mapping);
+ return null;
+ }
+
+ // otherwise use the context
+ if (contextId != null)
+ {
+ // Only log something when an actual context ID is used. Should solve FELIX-4307...
+ SystemLogger.debug("Reusing context with id [" + contextId + "]");
+ }
+
+ holder.addMapping(mapping);
+ return holder.getContext();
+ }
+
+ public synchronized void ungetHttpContext(Bundle bundle, String contextId, AbstractMapping mapping)
+ {
+ ungetHttpContext(bundle, contextId, mapping, false);
+ }
+
+ public synchronized void ungetHttpContext(Bundle bundle, String contextId,
+ AbstractMapping mapping, boolean isContextHelper)
+ {
+ // per-bundle context
+ String id = createId(bundle, contextId, isContextHelper);
+ HttpContextHolder context = this.idMap.get(id);
+
+ // shared context
+ if (context == null && !isContextHelper)
+ {
+ id = createId(contextId);
+ context = this.idMap.get(id);
+ }
+
+ // remove the mapping if there is a mapped context
+ if (context != null)
+ {
+ context.removeMapping(mapping);
+ }
+ else
+ {
+ Set<AbstractMapping> orphans = this.orphanMappings.get(contextId);
+ if (orphans != null)
+ {
+ orphans.remove(mapping);
+ if (orphans.isEmpty())
+ {
+ this.orphanMappings.remove(contextId);
+ }
+ }
+
+ // it is not expected but make sure there is no reference
+ mapping.setContext(null);
+ }
+ }
+
+ public synchronized Collection<AbstractMapping> addHttpContext(Bundle bundle, String contextId, HttpContext context)
+ {
+ String id = createId(bundle, contextId, false);
+ HttpContextHolder holder = new HttpContextHolder(context);
+
+ Set<AbstractMapping> orphans = this.orphanMappings.remove(contextId);
+ if (orphans != null)
+ {
+ for (Iterator<AbstractMapping> mi = orphans.iterator(); mi.hasNext();)
+ {
+ AbstractMapping mapping = mi.next();
+ if (bundle == null || bundle.equals(mapping.getBundle()))
+ {
+ holder.addMapping(mapping);
+ mi.remove();
+ }
+ }
+
+ // put any remaining orphans back
+ if (!orphans.isEmpty())
+ {
+ this.orphanMappings.put(contextId, orphans);
+ }
+ }
+
+ this.idMap.put(id, holder);
+ this.contextMap.put(context, id);
+
+ // return a copy to prevent concurrent modification
+ return new HashSet<AbstractMapping>(holder.getMappings());
+ }
+
+ public synchronized Collection<AbstractMapping> addContextHelper(Bundle bundle, String contextName, String contextPath,
+ ServletContextHelper contextHelper)
+ {
+ String id = createId(bundle, contextName, true);
+ HttpContextHolder holder = new HttpContextHolder(contextHelper, contextPath);
+
+ Set<AbstractMapping> orphans = this.orphanMappings.remove(contextName);
+ if (orphans != null)
+ {
+ for (Iterator<AbstractMapping> mi = orphans.iterator(); mi.hasNext();)
+ {
+ AbstractMapping mapping = mi.next();
+ if (bundle == null || bundle.equals(mapping.getBundle()))
+ {
+ holder.addMapping(mapping);
+ mi.remove();
+ }
+ }
+
+ // put any remaining orphans back
+ if (!orphans.isEmpty())
+ {
+ this.orphanMappings.put(contextName, orphans);
+ }
+ }
+
+ this.idMap.put(id, holder);
+ this.contextMap.put(holder.getContext(), id);
+ this.helperMap.put(contextHelper, holder.getContext());
+
+ return holder.getMappings();
+ }
+
+ public synchronized Collection<AbstractMapping> removeHttpContext(HttpContext context)
+ {
+ String id = this.contextMap.remove(context);
+ if (id != null)
+ {
+ HttpContextHolder holder = this.idMap.remove(id);
+ if (holder != null)
+ {
+ Set<AbstractMapping> mappings = holder.getMappings();
+ if (mappings != null && !mappings.isEmpty())
+ {
+ // keep the orphans around
+ final String contextId = getContextId(id);
+ Set<AbstractMapping> orphans = this.orphanMappings.get(contextId);
+ if (orphans == null)
+ {
+ orphans = new HashSet<AbstractMapping>();
+ this.orphanMappings.put(getContextId(id), orphans);
+ }
+
+ for (AbstractMapping mapping : mappings)
+ {
+ mapping.setContext(null);
+ orphans.add(mapping);
+ }
+ }
+ return mappings;
+ }
+ }
+ return null;
+ }
+
+ public synchronized Collection<AbstractMapping> removeContextHelper(ServletContextHelper contextHelper)
+ {
+ HttpContext context = this.helperMap.remove(contextHelper);
+ if (context != null)
+ {
+ return removeHttpContext(context);
+ }
+ return null;
+ }
+
+ synchronized Map<String, HttpContextHolder> getHttpContexts()
+ {
+ return new HashMap<String, HttpContextHolder>(this.idMap);
+ }
+
+ synchronized Map<String, Set<AbstractMapping>> getOrphanMappings()
+ {
+ return new HashMap<String, Set<AbstractMapping>>(this.orphanMappings);
+ }
+
+ static class HttpContextHolder
+ {
+ private final HttpContext context;
+ private final Set<AbstractMapping> mappings;
+ private final String path;
+
+ HttpContextHolder(final HttpContext context)
+ {
+ this.context = context;
+ this.mappings = new HashSet<AbstractMapping>();
+ this.path = null;
+ }
+
+ HttpContextHolder(final ServletContextHelper context,
+ final String path)
+ {
+ this.context = new HttpContextBridge(context);
+ this.mappings = new HashSet<AbstractMapping>();
+ this.path = path;
+ }
+
+ public HttpContext getContext()
+ {
+ return context;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ void addMapping(AbstractMapping mapping)
+ {
+ this.mappings.add(mapping);
+ mapping.setContext(getContext());
+ }
+
+ void removeMapping(AbstractMapping mapping)
+ {
+ mapping.setContext(null);
+ this.mappings.remove(mapping);
+ }
+
+ public Set<AbstractMapping> getMappings()
+ {
+ return mappings;
+ }
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ResourceMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ResourceMapping.java
new file mode 100644
index 0000000..72e371e
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ResourceMapping.java
@@ -0,0 +1,59 @@
+/*
+ * 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.whiteboard;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+
+public final class ResourceMapping extends AbstractMapping
+{
+ private final ResourceDTO dto;
+
+ public ResourceMapping(final Bundle bundle, final ResourceDTO resourceDTO)
+ {
+ super(bundle);
+ this.dto = resourceDTO;
+ }
+
+ @Override
+ public void register(final HttpService httpService)
+ {
+ if (!isRegistered() && getContext() != null)
+ {
+ try {
+ httpService.registerResources(this.dto.patterns[0], this.dto.prefix, this.getContext());
+ this.setRegistered(true);
+ }
+ catch (final NamespaceException e)
+ {
+ // TODO Handle exception
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void unregister(final HttpService httpService)
+ {
+ if (isRegistered())
+ {
+ httpService.unregister(this.dto.patterns[0]);
+ }
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletMapping.java
new file mode 100644
index 0000000..ff8b98b
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletMapping.java
@@ -0,0 +1,85 @@
+/*
+ * 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.whiteboard;
+
+import javax.servlet.Servlet;
+
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.service.HttpServiceImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpService;
+
+public final class ServletMapping extends AbstractMapping
+{
+ private final Servlet servlet;
+ private final ServletInfo servletInfo;
+
+ public ServletMapping(Bundle bundle, Servlet servlet, ServletInfo servletInfo)
+ {
+ super(bundle);
+ this.servlet = servlet;
+ this.servletInfo = servletInfo;
+ }
+
+ @Override
+ public void register(HttpService httpService)
+ {
+ if (!isRegistered() && (httpService instanceof HttpServiceImpl) && getContext() != null)
+ {
+ this.servletInfo.context = getContext(); // XXX
+ try
+ {
+ ((HttpServiceImpl) httpService).registerServlet(this.servlet, this.servletInfo);
+ setRegistered(true);
+ }
+ catch (Exception e)
+ {
+ // Warn that something might have gone astray...
+ SystemLogger.warning("Failed to register servlet for " + this.servletInfo.name, e);
+ }
+ setRegistered(true);
+ }
+ }
+
+ @Override
+ public void unregister(HttpService httpService)
+ {
+ if (isRegistered() && (httpService instanceof HttpServiceImpl))
+ {
+ try
+ {
+ ((HttpServiceImpl) httpService).unregisterServlet(this.servlet);
+ }
+ catch (Exception e)
+ {
+ // Warn that something might have gone astray...
+ SystemLogger.debug("Failed to unregister servlet for " + this.servletInfo.name, e);
+ }
+ finally
+ {
+ // Best effort: avoid mappings that are registered which is reality aren't registered...
+ setRegistered(false);
+ }
+ }
+ }
+
+ Servlet getServlet()
+ {
+ return this.servlet;
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/AbstractTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/AbstractTracker.java
new file mode 100644
index 0000000..62c3eea
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/AbstractTracker.java
@@ -0,0 +1,65 @@
+/*
+ * 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.whiteboard.tracker;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public abstract class AbstractTracker<T> extends ServiceTracker
+{
+ public AbstractTracker(final BundleContext context, final Class<T> clz)
+ {
+ super(context, clz.getName(), null);
+ }
+
+ public AbstractTracker(final BundleContext context, final Filter filter)
+ {
+ super(context, filter, null);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final Object addingService(final ServiceReference ref)
+ {
+ T service = (T) super.addingService(ref);
+ added(service, ref);
+ return service;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final void modifiedService(final ServiceReference ref, final Object service)
+ {
+ modified( (T)service, ref);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final void removedService(final ServiceReference ref, final Object service)
+ {
+ super.removedService(ref, service);
+ removed((T) service, ref);
+ }
+
+ protected abstract void modified(T service, ServiceReference ref);
+
+ protected abstract void added(T service, ServiceReference ref);
+
+ protected abstract void removed(T service, ServiceReference ref);
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java
new file mode 100644
index 0000000..7c5a576
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java
@@ -0,0 +1,72 @@
+/*
+ * 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.whiteboard.tracker;
+
+import javax.servlet.Filter;
+
+import org.apache.felix.http.base.internal.whiteboard.ExtenderManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public final class FilterTracker extends AbstractTracker<Filter>
+{
+ private final ExtenderManager manager;
+
+ private static org.osgi.framework.Filter createFilter(final BundleContext btx)
+ {
+ try
+ {
+ return btx.createFilter(String.format("(&(objectClass=%s)(|(%s=*)(%s=*)(%s=*)))",
+ Filter.class.getName(),
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN,
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX,
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET));
+ }
+ catch ( final InvalidSyntaxException ise)
+ {
+ // we can safely ignore it as the above filter is a constant
+ }
+ return null; // we never get here - and if we get an NPE which is fine
+ }
+
+ public FilterTracker(BundleContext context, ExtenderManager manager)
+ {
+ super(context, createFilter(context));
+ this.manager = manager;
+ }
+
+ @Override
+ protected void added(Filter service, ServiceReference ref)
+ {
+ this.manager.add(service, ref);
+ }
+
+ @Override
+ protected void modified(Filter service, ServiceReference ref)
+ {
+ removed(service, ref);
+ added(service, ref);
+ }
+
+ @Override
+ protected void removed(Filter service, ServiceReference ref)
+ {
+ this.manager.removeFilter(ref);
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ResourceTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ResourceTracker.java
new file mode 100644
index 0000000..fdf6bb3
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ResourceTracker.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.whiteboard.tracker;
+
+import org.apache.felix.http.base.internal.whiteboard.ExtenderManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public final class ResourceTracker extends AbstractTracker<Object>
+{
+ private final ExtenderManager manager;
+
+ private static Filter createFilter(final BundleContext btx)
+ {
+ try
+ {
+ return btx.createFilter(String.format("(&(%s=*)(%s=*))",
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN,
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX));
+ }
+ catch ( final InvalidSyntaxException ise)
+ {
+ // we can safely ignore it as the above filter is a constant
+ }
+ return null; // we never get here - and if we get an NPE which is fine
+ }
+
+ public ResourceTracker(final BundleContext context, final ExtenderManager manager)
+ {
+ super(context, createFilter(context));
+ this.manager = manager;
+ }
+
+ @Override
+ protected void added(final Object service, final ServiceReference ref)
+ {
+ this.manager.addResource(ref);
+ }
+
+ @Override
+ protected void modified(final Object service, final ServiceReference ref)
+ {
+ removed(service, ref);
+ added(service, ref);
+ }
+
+ @Override
+ protected void removed(final Object service, final ServiceReference ref)
+ {
+ this.manager.removeResource(ref);
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextHelperTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextHelperTracker.java
new file mode 100644
index 0000000..fd60d83
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextHelperTracker.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.whiteboard.tracker;
+
+import org.apache.felix.http.base.internal.whiteboard.ExtenderManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public final class ServletContextHelperTracker extends AbstractTracker<ServletContextHelper>
+{
+ private final ExtenderManager manager;
+
+ private static org.osgi.framework.Filter createFilter(final BundleContext btx)
+ {
+ try
+ {
+ return btx.createFilter(String.format("(&(objectClass=%s)(%s=*)(%s=*))",
+ ServletContextHelperTracker.class.getName(),
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME,
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH));
+ }
+ catch ( final InvalidSyntaxException ise)
+ {
+ // we can safely ignore it as the above filter is a constant
+ }
+ return null; // we never get here - and if we get an NPE which is fine
+ }
+
+ public ServletContextHelperTracker(final BundleContext context, ExtenderManager manager)
+ {
+ super(context, createFilter(context));
+ this.manager = manager;
+ }
+
+ @Override
+ protected void added(ServletContextHelper service, ServiceReference ref)
+ {
+ this.manager.add(service, ref);
+ }
+
+ @Override
+ protected void modified(ServletContextHelper service, ServiceReference ref)
+ {
+ removed(service, ref);
+ added(service, ref);
+ }
+
+ @Override
+ protected void removed(ServletContextHelper service, ServiceReference ref)
+ {
+ this.manager.remove(service);
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java
new file mode 100644
index 0000000..47360a5
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.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.whiteboard.tracker;
+
+import javax.servlet.Servlet;
+
+import org.apache.felix.http.base.internal.whiteboard.ExtenderManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public final class ServletTracker extends AbstractTracker<Servlet>
+{
+ private final ExtenderManager manager;
+
+ private static org.osgi.framework.Filter createFilter(final BundleContext btx)
+ {
+ try
+ {
+ return btx.createFilter(String.format("(&(objectClass=%s)(%s=*))",
+ Servlet.class.getName(),
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN));
+ }
+ catch ( final InvalidSyntaxException ise)
+ {
+ // we can safely ignore it as the above filter is a constant
+ }
+ return null; // we never get here - and if we get an NPE which is fine
+ }
+
+ public ServletTracker(final BundleContext context, final ExtenderManager manager)
+ {
+ super(context, createFilter(context));
+ this.manager = manager;
+ }
+
+ @Override
+ protected void added(Servlet service, ServiceReference ref)
+ {
+ this.manager.add(service, ref);
+ }
+
+ @Override
+ protected void modified(Servlet service, ServiceReference ref)
+ {
+ removed(service, ref);
+ added(service, ref);
+ }
+
+ @Override
+ protected void removed(Servlet service, ServiceReference ref)
+ {
+ this.manager.removeServlet(ref);
+ }
+}