FELIX-4545 : Implement Servlet Context Helper
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1656000 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
index 0bb738a..ee74584 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/AbstractInfo.java
@@ -24,7 +24,10 @@
import java.util.concurrent.atomic.AtomicLong;
import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
/**
* Base class for all info objects.
@@ -32,14 +35,19 @@
*/
public abstract class AbstractInfo<T> implements Comparable<AbstractInfo<T>>
{
+ /** Service id for services not provided through the service registry. */
+ private static final AtomicLong serviceIdCounter = new AtomicLong(-1);
+
/** Service ranking. */
private final int ranking;
/** Service id. */
private final long serviceId;
- /** Service id for services not provided through the service registry. */
- private static final AtomicLong serviceIdCounter = new AtomicLong(-1);
+ /** The context selection. */
+ private final String contextSelection;
+
+ private final Filter filter;
/** Service reference. */
private final ServiceReference<T> serviceReference;
@@ -57,13 +65,32 @@
this.ranking = 0;
}
this.serviceReference = ref;
+ String sel = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT);
+ if ( isEmpty(sel) )
+ {
+ this.contextSelection = "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "="
+ + HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME + ")";
+ }
+ else
+ {
+ this.contextSelection = sel;
+ }
+ Filter f = null;
+ try
+ {
+ f = ref.getBundle().getBundleContext().createFilter(this.contextSelection);
+ }
+ catch ( final InvalidSyntaxException ise)
+ {
+ // ignore
+ f = null;
+ }
+ this.filter = f;
}
public AbstractInfo(final int ranking)
{
- this.ranking = ranking;
- this.serviceId = serviceIdCounter.getAndDecrement();
- this.serviceReference = null;
+ this(ranking, serviceIdCounter.getAndDecrement());
}
@@ -72,6 +99,12 @@
this.ranking = ranking;
this.serviceId = serviceId;
this.serviceReference = null;
+ this.contextSelection = null;
+ this.filter = null;
+ }
+
+ public boolean isValid() {
+ return this.filter != null || this.serviceReference == null;
}
/**
@@ -191,4 +224,42 @@
{
return this.serviceReference;
}
+
+ public String getContextSelection()
+ {
+ return this.contextSelection;
+ }
+
+ public Filter getContextSelectionFilter()
+ {
+ return this.filter;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ranking;
+ result = prime * result + (int) (serviceId ^ (serviceId >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ @SuppressWarnings("unchecked")
+ final AbstractInfo<T> other = (AbstractInfo<T>) obj;
+ if (ranking != other.ranking)
+ return false;
+ if (serviceId != other.serviceId)
+ return false;
+ return true;
+ }
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
index d10e7d5..5e046eb 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
@@ -37,20 +37,11 @@
this.path = this.getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH);
}
- /**
- * Create an info object for the default context.
- */
- public ContextInfo()
- {
- super(0, Long.MIN_VALUE);
- this.name = HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME;
- this.path = "/";
- }
-
+ @Override
public boolean isValid()
{
// TODO - check if name and path are valid values
- return !this.isEmpty(this.name) && !this.isEmpty(this.path);
+ return super.isValid() && !this.isEmpty(this.name) && !this.isEmpty(this.path);
}
public String getName()
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
index b352faf..12dfdb8 100644
--- 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
@@ -108,9 +108,10 @@
this.context = context;
}
+ @Override
public boolean isValid()
{
- return !isEmpty(this.patterns);
+ return super.isValid() && !isEmpty(this.patterns);
}
public String getName()
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
index bc9e4c1..308685c 100644
--- 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
@@ -65,7 +65,7 @@
public ExtenderManager(final HttpServiceImpl httpService, final BundleContext bundleContext)
{
this.mapping = new HashMap<String, AbstractMapping>();
- this.contextManager = new ServletContextHelperManager(httpService);
+ this.contextManager = new ServletContextHelperManager(bundleContext, httpService);
this.httpService = httpService;
addTracker(new FilterTracker(bundleContext, this));
addTracker(new ServletTracker(bundleContext, this.contextManager));
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
index bc35748..0c804b4 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
@@ -18,48 +18,102 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.HashMap;
+import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import org.apache.felix.http.base.internal.runtime.AbstractInfo;
import org.apache.felix.http.base.internal.runtime.ContextInfo;
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.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
public final class ServletContextHelperManager
{
- private final Map<String, List<ContextHolder>> nameMap = new HashMap<String, List<ContextHolder>>();
+ /** A map containing all servlet context registrations. Mapped by context name */
+ private final Map<String, List<ContextHolder>> contextMap = new HashMap<String, List<ContextHolder>>();
+
+ /** A map with all servlet registrations, mapped by servlet info. */
+ private final Map<ServletInfo, List<ContextHolder>> servletList = new HashMap<ServletInfo, List<ContextHolder>>();
private final HttpServiceImpl httpService;
+ private final ServiceRegistration<ServletContextHelper> defaultContextRegistration;
/**
* Create a new servlet context helper manager
* and the default context
*/
- public ServletContextHelperManager(final HttpServiceImpl httpService)
+ public ServletContextHelperManager(final BundleContext bundleContext, final HttpServiceImpl httpService)
{
this.httpService = httpService;
- // create default context
- final ContextInfo info = new ContextInfo();
- this.addContextHelper(info);
+ final Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME);
+ props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/");
+
+ this.defaultContextRegistration = bundleContext.registerService(ServletContextHelper.class,
+ new ServiceFactory<ServletContextHelper>() {
+
+ @Override
+ public ServletContextHelper getService(
+ final Bundle bundle,
+ final ServiceRegistration<ServletContextHelper> registration) {
+ return new ServletContextHelper(bundle) {
+ };
+ }
+
+ @Override
+ public void ungetService(
+ final Bundle bundle,
+ final ServiceRegistration<ServletContextHelper> registration,
+ final ServletContextHelper service) {
+ // nothing to do
+ }
+ },
+ props);
}
public void close()
{
- // TODO Auto-generated method stub
+ // TODO cleanup
+ this.defaultContextRegistration.unregister();
}
private void activate(final ContextHolder holder)
{
- // TODO
+ for(final Map.Entry<ServletInfo, List<ContextHolder>> entry : this.servletList.entrySet())
+ {
+ if ( entry.getKey().getContextSelectionFilter().match(holder.getInfo().getServiceReference()) )
+ {
+ entry.getValue().add(holder);
+ this.httpService.registerServlet(entry.getKey());
+ }
+ }
}
private void deactivate(final ContextHolder holder)
{
- // TODO
+ final Iterator<Map.Entry<ServletInfo, List<ContextHolder>>> i = this.servletList.entrySet().iterator();
+ while ( i.hasNext() )
+ {
+ final Map.Entry<ServletInfo, List<ContextHolder>> entry = i.next();
+ if ( entry.getValue().remove(holder) )
+ {
+ this.httpService.unregisterServlet(entry.getKey());
+ if ( entry.getValue().isEmpty() ) {
+ i.remove();
+ }
+ }
+ }
}
/**
@@ -68,13 +122,13 @@
public void addContextHelper(final ContextInfo info)
{
final ContextHolder holder = new ContextHolder(info);
- synchronized ( this.nameMap )
+ synchronized ( this.contextMap )
{
- List<ContextHolder> holderList = this.nameMap.get(info.getName());
+ List<ContextHolder> holderList = this.contextMap.get(info.getName());
if ( holderList == null )
{
holderList = new ArrayList<ContextHolder>();
- this.nameMap.put(info.getName(), holderList);
+ this.contextMap.put(info.getName(), holderList);
}
holderList.add(holder);
Collections.sort(holderList);
@@ -96,9 +150,9 @@
*/
public void removeContextHelper(final ContextInfo info)
{
- synchronized ( this.nameMap )
+ synchronized ( this.contextMap )
{
- final List<ContextHolder> holderList = this.nameMap.get(info.getName());
+ final List<ContextHolder> holderList = this.contextMap.get(info.getName());
if ( holderList != null )
{
final Iterator<ContextHolder> i = holderList.iterator();
@@ -122,7 +176,7 @@
}
if ( holderList.isEmpty() )
{
- this.nameMap.remove(info.getName());
+ this.contextMap.remove(info.getName());
}
else if ( activateNext )
{
@@ -132,16 +186,58 @@
}
}
+ private List<ContextHolder> getMatchingContexts(final AbstractInfo<?> info)
+ {
+ final List<ContextHolder> result = new ArrayList<ContextHolder>();
+ for(final List<ContextHolder> holders : this.contextMap.values()) {
+ final ContextHolder h = holders.get(0);
+ if ( info.getContextSelectionFilter().match(h.getInfo().getServiceReference()) )
+ {
+ result.add(h);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Add a new servlet.
+ * @param servletInfo The servlet info
+ */
public void addServlet(final ServletInfo servletInfo)
{
- this.httpService.registerServlet(servletInfo);
+ synchronized ( this.contextMap )
+ {
+ final List<ContextHolder> holderList = this.getMatchingContexts(servletInfo);
+ this.servletList.put(servletInfo, holderList);
+ for(final ContextHolder h : holderList)
+ {
+ this.httpService.registerServlet(servletInfo);
+ }
+ }
}
+ /**
+ * Remove a servlet
+ * @param servletInfo The servlet info
+ */
public void removeServlet(final ServletInfo servletInfo)
{
- this.httpService.unregisterServlet(servletInfo);
+ synchronized ( this.contextMap )
+ {
+ final List<ContextHolder> holderList = this.servletList.remove(servletInfo);
+ if ( holderList != null )
+ {
+ for(final ContextHolder h : holderList)
+ {
+ this.httpService.unregisterServlet(servletInfo);
+ }
+ }
+ }
}
+ /**
+ * Hold information about a context.
+ */
private final static class ContextHolder implements Comparable<ContextHolder>
{
private final ContextInfo info;
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
index 269e607..f73ca2d 100644
--- 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
@@ -39,7 +39,7 @@
try
{
return btx.createFilter(String.format("(&(objectClass=%s)(%s=*)(%s=*))",
- ServletContextHelperTracker.class.getName(),
+ ServletContextHelper.class.getName(),
HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME,
HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH));
}