FELIX-4782 : Implement session handling
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1659596 13f79535-47bb-0310-9956-ffa450edef68
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
index 48d88da..81a3149 100644
--- 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
@@ -19,11 +19,15 @@
import java.util.Hashtable;
import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
import org.apache.felix.http.api.ExtHttpService;
import org.apache.felix.http.base.internal.dispatch.Dispatcher;
import org.apache.felix.http.base.internal.handler.HandlerRegistry;
import org.apache.felix.http.base.internal.handler.HttpServicePlugin;
+import org.apache.felix.http.base.internal.handler.HttpSessionWrapper;
import org.apache.felix.http.base.internal.listener.HttpSessionAttributeListenerManager;
import org.apache.felix.http.base.internal.listener.HttpSessionListenerManager;
import org.apache.felix.http.base.internal.listener.ServletContextAttributeListenerManager;
@@ -116,12 +120,24 @@
return requestAttributeListener;
}
- public HttpSessionListenerManager getSessionListener()
+ public HttpSessionListener getSessionListener()
{
- return sessionListener;
+ return new HttpSessionListener() {
+
+ @Override
+ public void sessionDestroyed(final HttpSessionEvent se) {
+ sessionListener.sessionDestroyed(se);
+ whiteboardHttpService.sessionDestroyed(se.getSession(), HttpSessionWrapper.getSessionContextIds(se.getSession()));
+ }
+
+ @Override
+ public void sessionCreated(final HttpSessionEvent se) {
+ sessionListener.sessionCreated(se);
+ }
+ };
}
- public HttpSessionAttributeListenerManager getSessionAttributeListener()
+ public HttpSessionAttributeListener getSessionAttributeListener()
{
return sessionAttributeListener;
}
@@ -175,10 +191,12 @@
servletContext,
this.registry,
this.runtimeServiceReg.getReference());
+ this.dispatcher.setWhiteboardHttpService(this.whiteboardHttpService);
}
public void unregister()
{
+ this.dispatcher.setWhiteboardHttpService(null);
if ( this.whiteboardHttpService != null )
{
this.whiteboardHttpService.close();
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
index fc7ec8b..ef9f9a0 100644
--- 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
@@ -21,8 +21,14 @@
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionListener;
public interface ExtServletContext extends ServletContext
{
boolean handleSecurity(HttpServletRequest req, HttpServletResponse res) throws IOException;
+
+ HttpSessionAttributeListener getHttpSessionAttributeListener();
+
+ HttpSessionListener getHttpSessionListener();
}
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
index 3eba855..23b1a24 100644
--- 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
@@ -41,6 +41,8 @@
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionListener;
import org.apache.felix.http.base.internal.logger.SystemLogger;
import org.apache.felix.http.base.internal.util.MimeTypes;
@@ -380,6 +382,18 @@
}
@Override
+ public HttpSessionListener getHttpSessionListener()
+ {
+ return null;
+ }
+
+ @Override
+ public HttpSessionAttributeListener getHttpSessionAttributeListener()
+ {
+ return null;
+ }
+
+ @Override
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/dispatch/Dispatcher.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
index cc265f5..c657215 100644
--- 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
@@ -31,6 +31,7 @@
import static org.apache.felix.http.base.internal.util.UriUtils.removeDotSegments;
import java.io.IOException;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.DispatcherType;
@@ -53,6 +54,7 @@
import org.apache.felix.http.base.internal.handler.HttpSessionWrapper;
import org.apache.felix.http.base.internal.handler.ServletHandler;
import org.apache.felix.http.base.internal.util.UriUtils;
+import org.apache.felix.http.base.internal.whiteboard.WhiteboardHttpService;
import org.osgi.service.http.HttpContext;
import org.osgi.service.useradmin.Authorization;
@@ -452,7 +454,12 @@
{
return null;
}
- return new HttpSessionWrapper(this.contextId, session, this.servletContext);
+ // check if internal session is available
+ if ( !create && !HttpSessionWrapper.hasSession(this.contextId, session) )
+ {
+ return null;
+ }
+ return new HttpSessionWrapper(this.contextId, session, this.servletContext, false);
}
@Override
@@ -516,11 +523,18 @@
private final HandlerRegistry handlerRegistry;
- public Dispatcher(HandlerRegistry handlerRegistry)
+ private WhiteboardHttpService whiteboardService;
+
+ public Dispatcher(final HandlerRegistry handlerRegistry)
{
this.handlerRegistry = handlerRegistry;
}
+ public void setWhiteboardHttpService(final WhiteboardHttpService service)
+ {
+ this.whiteboardService = service;
+ }
+
/**
* Responsible for dispatching a given request to the actual applicable servlet and/or filters in the local registry.
*
@@ -531,6 +545,13 @@
*/
public void dispatch(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException
{
+ // invalid sessions first
+ final HttpSession session = req.getSession(false);
+ if ( session != null )
+ {
+ final Set<Long> ids = HttpSessionWrapper.getExpiredSessionContextIds(session);
+ this.whiteboardService.sessionDestroyed(session, ids);
+ }
String requestURI = getRequestURI(req);
// Determine which servlets we should forward the request to...
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
index 2573cca..346c53c 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
@@ -19,70 +19,184 @@
package org.apache.felix.http.base.internal.handler;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Enumeration;
-import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+
+import org.apache.felix.http.base.internal.context.ExtServletContext;
/**
+ * The session wrapper keeps track of the internal session, manages their attributes
+ * separately and also handles session timeout.
+ *
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-@SuppressWarnings("deprecation")
public class HttpSessionWrapper implements HttpSession
{
+ /** All special attributes are prefixed with this prefix. */
private static final String PREFIX = "org.apache.felix.http.session.context.";
- private static final String SESSION_MAP = "org.apache.felix.http.session.context.map";
+ /** For each internal session, the attributes are prefixed with this followed by the context id */
+ private static final String ATTR_PREFIX = PREFIX + "attr.";
+ /** The created time for the internal session (appended with context id) */
+ private static final String ATTR_CREATED = PREFIX + "created.";
+
+ /** The last accessed time for the internal session (appended with context id) */
+ private static final String ATTR_LAST_ACCESSED = PREFIX + "lastaccessed.";
+
+ /** The max inactive time (appended with context id) */
+ private static final String ATTR_MAX_INACTIVE = PREFIX + "maxinactive.";
+
+ /** The underlying container session. */
private final HttpSession delegate;
- private final ServletContext context;
+ /** The corresponding servlet context. */
+ private final ExtServletContext context;
+
+ /** The id for this session. */
+ private final String sessionId;
+
+ /** The key prefix for attributes belonging to this session. */
private final String keyPrefix;
+ /** Flag to handle the validity of this session. */
private volatile boolean isInvalid = false;
+ /** The time this has been created. */
private final long created;
- private final long contextId;
+ /** The time this has been last accessed. */
+ private final long lastAccessed;
+
+ /** The max timeout interval. */
+ private int maxTimeout;
+
+ /**
+ * Is this a new session?
+ */
+ private final boolean isNew;
+
+ public static boolean hasSession(final Long contextId, final HttpSession session)
+ {
+ final String sessionId = contextId == null ? "0" : String.valueOf(contextId);
+ return session.getAttribute(ATTR_CREATED + sessionId) != null;
+ }
+
+ public static Set<Long> getExpiredSessionContextIds(final HttpSession session)
+ {
+ final long now = System.currentTimeMillis();
+ final Set<Long> ids = new HashSet<Long>();
+ final Enumeration<String> names = session.getAttributeNames();
+ while ( names.hasMoreElements() )
+ {
+ final String name = names.nextElement();
+ if ( name.startsWith(ATTR_LAST_ACCESSED) )
+ {
+ final String id = name.substring(ATTR_LAST_ACCESSED.length());
+
+ final long lastAccess = (Long)session.getAttribute(name);
+ final Integer maxTimeout = (Integer)session.getAttribute(ATTR_MAX_INACTIVE + id);
+
+ if ( lastAccess + maxTimeout < now )
+ {
+ ids.add(Long.valueOf(id));
+ }
+ }
+ }
+ return ids;
+ }
+
+ public static Set<Long> getSessionContextIds(final HttpSession session)
+ {
+ final Set<Long> ids = new HashSet<Long>();
+ final Enumeration<String> names = session.getAttributeNames();
+ while ( names.hasMoreElements() )
+ {
+ final String name = names.nextElement();
+ if ( name.startsWith(ATTR_LAST_ACCESSED) )
+ {
+ final String id = name.substring(ATTR_LAST_ACCESSED.length());
+ ids.add(Long.valueOf(id));
+ }
+ }
+ return ids;
+ }
/**
* Creates a new {@link HttpSessionWrapper} instance.
*/
- public HttpSessionWrapper(final Long contextId, final HttpSession session,
- final ServletContext context)
+ public HttpSessionWrapper(final Long contextId,
+ final HttpSession session,
+ final ExtServletContext context,
+ final boolean terminate)
{
this.delegate = session;
this.context = context;
- this.contextId = (contextId == null ? 0 : contextId);
- this.keyPrefix = this.contextId == 0 ? null : PREFIX + String.valueOf(contextId) + ".";
- synchronized ( this.getClass() )
+ this.sessionId = contextId == null ? "0" : String.valueOf(contextId);
+ this.keyPrefix = contextId == null ? null : ATTR_PREFIX + this.sessionId + ".";
+
+ if ( this.keyPrefix != null )
{
- @SuppressWarnings("unchecked")
- Map<Long, Long> sessionMap = (Map<Long, Long>)session.getAttribute(SESSION_MAP);
- if ( sessionMap == null )
+ final long now = System.currentTimeMillis();
+ if ( session.getAttribute(ATTR_CREATED + this.sessionId) == null )
{
- sessionMap = new HashMap<Long, Long>();
+ this.created = now;
+ this.maxTimeout = session.getMaxInactiveInterval();
+ isNew = true;
+
+ session.setAttribute(ATTR_CREATED + this.sessionId, this.created);
+ session.setAttribute(ATTR_MAX_INACTIVE + this.sessionId, this.maxTimeout);
+
+ if ( context.getHttpSessionListener() != null )
+ {
+ context.getHttpSessionListener().sessionCreated(new HttpSessionEvent(this));
+ }
}
- if ( !sessionMap.containsKey(contextId) )
+ else
{
- sessionMap.put(contextId, System.currentTimeMillis());
- session.setAttribute(SESSION_MAP, sessionMap);
+ this.created = (Long)session.getAttribute(ATTR_CREATED + this.sessionId);
+ this.maxTimeout = (Integer)session.getAttribute(ATTR_MAX_INACTIVE + this.sessionId);
+ isNew = false;
}
- this.created = sessionMap.get(contextId);
+
+ this.lastAccessed = now;
+ if ( !terminate )
+ {
+ session.setAttribute(ATTR_LAST_ACCESSED + this.sessionId, this.lastAccessed);
+ }
+ }
+ else
+ {
+ this.isNew = session.isNew();
+ this.lastAccessed = session.getLastAccessedTime();
+ this.created = session.getCreationTime();
}
}
+ /**
+ * Helper method to get the real key within the real session.
+ */
private String getKey(final String name)
{
return this.keyPrefix == null ? name : this.keyPrefix.concat(name);
}
+ /**
+ * Check whether this session is still valid.
+ * @throws IllegalStateException if session is not valid anymore
+ */
private void checkInvalid()
{
if ( this.isInvalid )
@@ -95,7 +209,12 @@
public Object getAttribute(final String name)
{
this.checkInvalid();
- return this.delegate.getAttribute(this.getKey(name));
+ Object result = this.delegate.getAttribute(this.getKey(name));
+ if ( result instanceof SessionBindingValueListenerWrapper )
+ {
+ result = ((SessionBindingValueListenerWrapper)result).getHttpSessionBindingListener();
+ }
+ return result;
}
@Override
@@ -112,7 +231,7 @@
while ( e.hasMoreElements() )
{
final String name = e.nextElement();
- if ( keyPrefix == null )
+ if ( keyPrefix == null && !name.startsWith(PREFIX) )
{
return name;
}
@@ -154,37 +273,31 @@
public String getId()
{
this.checkInvalid();
- return this.delegate.getId() + "-" + String.valueOf(this.contextId);
+ return this.delegate.getId() + "-" + this.sessionId;
}
@Override
public long getLastAccessedTime()
{
this.checkInvalid();
- // TODO
- return this.delegate.getLastAccessedTime();
+ return this.lastAccessed;
}
@Override
public int getMaxInactiveInterval()
{
- // TODO
- return this.delegate.getMaxInactiveInterval();
+ // no validity check conforming to the javadocs
+ return this.maxTimeout;
}
@Override
public ServletContext getServletContext()
{
+ // no validity check conforming to the javadocs
return this.context;
}
@Override
- public HttpSessionContext getSessionContext()
- {
- return this.delegate.getSessionContext();
- }
-
- @Override
public Object getValue(String name)
{
return this.getAttribute(name);
@@ -206,16 +319,43 @@
public void invalidate()
{
this.checkInvalid();
+
+ if ( this.keyPrefix != null )
+ {
+ this.delegate.removeAttribute(ATTR_CREATED + this.sessionId);
+ this.delegate.removeAttribute(ATTR_LAST_ACCESSED + this.sessionId);
+ this.delegate.removeAttribute(ATTR_MAX_INACTIVE + this.sessionId);
+
+ final Enumeration<String> names = this.delegate.getAttributeNames();
+ while ( names.hasMoreElements() )
+ {
+ final String name = names.nextElement();
+
+ if ( name.startsWith(this.keyPrefix) ) {
+ this.removeAttribute(name.substring(this.keyPrefix.length()));
+ }
+ }
+ }
+
+ // if the session is empty we can invalidate
+ final Enumeration<String> names = this.delegate.getAttributeNames();
+ if ( !names.hasMoreElements() )
+ {
+ this.delegate.invalidate();
+ }
+
+ if ( context.getHttpSessionListener() != null )
+ {
+ context.getHttpSessionListener().sessionDestroyed(new HttpSessionEvent(this));
+ }
this.isInvalid = true;
- // TODO
- this.delegate.invalidate();
}
@Override
public boolean isNew()
{
this.checkInvalid();
- return this.delegate.isNew();
+ return this.isNew;
}
@Override
@@ -228,26 +368,97 @@
public void removeAttribute(final String name)
{
this.checkInvalid();
- this.delegate.removeAttribute(name);
+ final Object oldValue = this.getAttribute(name);
+ if ( oldValue != null )
+ {
+ this.delegate.removeAttribute(this.getKey(name));
+ if ( this.keyPrefix != null && oldValue instanceof HttpSessionBindingListener )
+ {
+ ((HttpSessionBindingListener)oldValue).valueUnbound(new HttpSessionBindingEvent(this, name));
+ }
+ if ( this.context.getHttpSessionAttributeListener() != null )
+ {
+ this.context.getHttpSessionAttributeListener().attributeRemoved(new HttpSessionBindingEvent(this, name, oldValue));
+ }
+ }
}
@Override
public void removeValue(final String name)
{
- this.removeAttribute(this.getKey(name));
+ this.removeAttribute(name);
}
@Override
public void setAttribute(final String name, final Object value)
{
this.checkInvalid();
- this.delegate.setAttribute(this.getKey(name), value);
+ final Object oldValue = this.getAttribute(name);
+ // wrap http session binding listener to avoid container calling it!
+ if ( this.keyPrefix != null && value instanceof HttpSessionBindingListener )
+ {
+ this.delegate.setAttribute(this.getKey(name),
+ new SessionBindingValueListenerWrapper((HttpSessionBindingListener)value));
+ }
+ else
+ {
+ this.delegate.setAttribute(this.getKey(name), value);
+ }
+ if ( this.keyPrefix != null && value instanceof HttpSessionBindingListener )
+ {
+ ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this, name));
+ }
+
+ if ( this.context.getHttpSessionAttributeListener() != null )
+ {
+ if ( oldValue != null )
+ {
+ this.context.getHttpSessionAttributeListener().attributeReplaced(new HttpSessionBindingEvent(this, name, oldValue));
+ }
+ else
+ {
+ this.context.getHttpSessionAttributeListener().attributeAdded(new HttpSessionBindingEvent(this, name, value));
+ }
+ }
}
@Override
- public void setMaxInactiveInterval(int interval)
+ public void setMaxInactiveInterval(final int interval)
{
- // TODO
- this.delegate.setMaxInactiveInterval(interval);
+ if ( this.delegate.getMaxInactiveInterval() < interval )
+ {
+ this.delegate.setMaxInactiveInterval(interval);
+ }
+ if ( this.keyPrefix != null )
+ {
+ this.maxTimeout = interval;
+ this.delegate.setAttribute(ATTR_MAX_INACTIVE + this.sessionId, interval);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public HttpSessionContext getSessionContext()
+ {
+ // no need to check validity conforming to the javadoc
+ return this.delegate.getSessionContext();
+ }
+
+ private static final class SessionBindingValueListenerWrapper implements Serializable
+ {
+
+ private static final long serialVersionUID = 4009563108883768425L;
+
+ private final HttpSessionBindingListener listener;
+
+ public SessionBindingValueListenerWrapper(final HttpSessionBindingListener listener)
+ {
+ this.listener = listener;
+ }
+
+ public HttpSessionBindingListener getHttpSessionBindingListener()
+ {
+ return listener;
+ }
}
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
index fed1f4b..af5bc80 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
@@ -18,7 +18,7 @@
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListMap;
import javax.annotation.Nonnull;
import javax.servlet.ServletContext;
@@ -26,13 +26,20 @@
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.runtime.HttpSessionAttributeListenerInfo;
+import org.apache.felix.http.base.internal.runtime.HttpSessionListenerInfo;
import org.apache.felix.http.base.internal.runtime.ServletContextAttributeListenerInfo;
import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
import org.apache.felix.http.base.internal.runtime.ServletContextListenerInfo;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceObjects;
+import org.osgi.framework.ServiceReference;
import org.osgi.service.http.context.ServletContextHelper;
public final class ContextHandler implements Comparable<ContextHandler>
@@ -50,7 +57,16 @@
private final Map<Long, ServletContextListener> listeners = new HashMap<Long, ServletContextListener>();
/** Servlet context attribute listeners. */
- private final Map<Long, ServletContextAttributeListener> contextAttributeListeners = new ConcurrentHashMap<Long, ServletContextAttributeListener>();
+ private final Map<ServiceReference<ServletContextAttributeListener>, ServletContextAttributeListener> contextAttributeListeners =
+ new ConcurrentSkipListMap<ServiceReference<ServletContextAttributeListener>, ServletContextAttributeListener>();
+
+ /** Session attribute listeners. */
+ private final Map<ServiceReference<HttpSessionAttributeListener>, HttpSessionAttributeListener> sessionAttributeListeners =
+ new ConcurrentSkipListMap<ServiceReference<HttpSessionAttributeListener>, HttpSessionAttributeListener>();
+
+ /** Session listeners. */
+ private final Map<ServiceReference<HttpSessionListener>, HttpSessionListener> sessionListeners =
+ new ConcurrentSkipListMap<ServiceReference<HttpSessionListener>, HttpSessionListener>();
/** The http bundle. */
private final Bundle bundle;
@@ -70,32 +86,7 @@
info.getName(),
info.getPrefix(),
info.getInitParameters(),
- new ServletContextAttributeListener() {
-
- @Override
- public void attributeReplaced(final ServletContextAttributeEvent event) {
- for(final ServletContextAttributeListener l : contextAttributeListeners.values())
- {
- l.attributeReplaced(event);
- }
- }
-
- @Override
- public void attributeRemoved(final ServletContextAttributeEvent event) {
- for(final ServletContextAttributeListener l : contextAttributeListeners.values())
- {
- l.attributeReplaced(event);
- }
- }
-
- @Override
- public void attributeAdded(final ServletContextAttributeEvent event) {
- for(final ServletContextAttributeListener l : contextAttributeListeners.values())
- {
- l.attributeReplaced(event);
- }
- }
- });
+ getContextAttributeListener());
}
/**
@@ -175,7 +166,9 @@
holder.servletContextHelper = so.getService();
holder.servletContext = new PerBundleServletContextImpl(bundle,
this.sharedContext,
- holder.servletContextHelper);
+ holder.servletContextHelper,
+ this.getSessionListener(),
+ this.getSessionAttributeListener());
this.contextMap.put(key, holder);
}
}
@@ -219,7 +212,7 @@
final ServletContextAttributeListener service = bundle.getBundleContext().getServiceObjects(info.getServiceReference()).getService();
if ( service != null )
{
- this.contextAttributeListeners.put(info.getServiceId(), service);
+ this.contextAttributeListeners.put(info.getServiceReference(), service);
}
}
}
@@ -230,7 +223,7 @@
*/
public void removeListener(@Nonnull final ServletContextAttributeListenerInfo info)
{
- final ServletContextAttributeListener service = this.contextAttributeListeners.remove(info.getServiceId());
+ final ServletContextAttributeListener service = this.contextAttributeListeners.remove(info.getServiceReference());
if ( service != null )
{
final ServiceObjects<ServletContextAttributeListener> so = bundle.getBundleContext().getServiceObjects(info.getServiceReference());
@@ -240,6 +233,72 @@
}
}
+ /**
+ * Add session attribute listener
+ * @param info
+ */
+ public void addListener(@Nonnull final HttpSessionAttributeListenerInfo info)
+ {
+ final ServiceObjects<HttpSessionAttributeListener> so = bundle.getBundleContext().getServiceObjects(info.getServiceReference());
+ if ( so != null )
+ {
+ final HttpSessionAttributeListener service = bundle.getBundleContext().getServiceObjects(info.getServiceReference()).getService();
+ if ( service != null )
+ {
+ this.sessionAttributeListeners.put(info.getServiceReference(), service);
+ }
+ }
+ }
+
+ /**
+ * Remove session attribute listener
+ * @param info
+ */
+ public void removeListener(@Nonnull final HttpSessionAttributeListenerInfo info)
+ {
+ final HttpSessionAttributeListener service = this.sessionAttributeListeners.remove(info.getServiceReference());
+ if ( service != null )
+ {
+ final ServiceObjects<HttpSessionAttributeListener> so = bundle.getBundleContext().getServiceObjects(info.getServiceReference());
+ if ( so != null ) {
+ so.ungetService(service);
+ }
+ }
+ }
+
+ /**
+ * Add session listener
+ * @param info
+ */
+ public void addListener(@Nonnull final HttpSessionListenerInfo info)
+ {
+ final ServiceObjects<HttpSessionListener> so = bundle.getBundleContext().getServiceObjects(info.getServiceReference());
+ if ( so != null )
+ {
+ final HttpSessionListener service = bundle.getBundleContext().getServiceObjects(info.getServiceReference()).getService();
+ if ( service != null )
+ {
+ this.sessionListeners.put(info.getServiceReference(), service);
+ }
+ }
+ }
+
+ /**
+ * Remove session listener
+ * @param info
+ */
+ public void removeListener(@Nonnull final HttpSessionListenerInfo info)
+ {
+ final HttpSessionListener service = this.sessionListeners.remove(info.getServiceReference());
+ if ( service != null )
+ {
+ final ServiceObjects<HttpSessionListener> so = bundle.getBundleContext().getServiceObjects(info.getServiceReference());
+ if ( so != null ) {
+ so.ungetService(service);
+ }
+ }
+ }
+
private static final class ContextHolder
{
public long counter;
@@ -247,4 +306,85 @@
public ServletContextHelper servletContextHelper;
}
+ public HttpSessionAttributeListener getSessionAttributeListener()
+ {
+ return new HttpSessionAttributeListener() {
+
+ @Override
+ public void attributeReplaced(final HttpSessionBindingEvent event) {
+ for(final HttpSessionAttributeListener l : sessionAttributeListeners.values())
+ {
+ l.attributeReplaced(event);
+ }
+ }
+
+ @Override
+ public void attributeRemoved(final HttpSessionBindingEvent event) {
+ for(final HttpSessionAttributeListener l : sessionAttributeListeners.values())
+ {
+ l.attributeReplaced(event);
+ }
+ }
+
+ @Override
+ public void attributeAdded(final HttpSessionBindingEvent event) {
+ for(final HttpSessionAttributeListener l : sessionAttributeListeners.values())
+ {
+ l.attributeReplaced(event);
+ }
+ }
+ };
+ }
+
+ private ServletContextAttributeListener getContextAttributeListener()
+ {
+ return new ServletContextAttributeListener() {
+
+ @Override
+ public void attributeReplaced(final ServletContextAttributeEvent event) {
+ for(final ServletContextAttributeListener l : contextAttributeListeners.values())
+ {
+ l.attributeReplaced(event);
+ }
+ }
+
+ @Override
+ public void attributeRemoved(final ServletContextAttributeEvent event) {
+ for(final ServletContextAttributeListener l : contextAttributeListeners.values())
+ {
+ l.attributeReplaced(event);
+ }
+ }
+
+ @Override
+ public void attributeAdded(final ServletContextAttributeEvent event) {
+ for(final ServletContextAttributeListener l : contextAttributeListeners.values())
+ {
+ l.attributeReplaced(event);
+ }
+ }
+ };
+ }
+
+ public HttpSessionListener getSessionListener()
+ {
+ return new HttpSessionListener() {
+
+ @Override
+ public void sessionCreated(final HttpSessionEvent se) {
+ for(final HttpSessionListener l : sessionListeners.values())
+ {
+ l.sessionCreated(se);
+ }
+ }
+
+ @Override
+ public void sessionDestroyed(final HttpSessionEvent se) {
+ for(final HttpSessionListener l : sessionListeners.values())
+ {
+ l.sessionDestroyed(se);
+ }
+ }
+ };
+ }
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerBundleServletContextImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerBundleServletContextImpl.java
index 7fce2e4..e64db90 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerBundleServletContextImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerBundleServletContextImpl.java
@@ -37,6 +37,8 @@
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionListener;
import org.apache.felix.http.base.internal.context.ExtServletContext;
import org.osgi.framework.Bundle;
@@ -54,14 +56,20 @@
private final Bundle bundle;
private final ServletContext delegatee;
private final ServletContextHelper contextHelper;
+ private final HttpSessionListener sessionListener;
+ private final HttpSessionAttributeListener sessionAttributeListener;
public PerBundleServletContextImpl(final Bundle bundle,
final ServletContext sharedContext,
- final ServletContextHelper delegatee)
+ final ServletContextHelper delegatee,
+ final HttpSessionListener sessionListener,
+ final HttpSessionAttributeListener sessionAttributeListener)
{
this.bundle = bundle;
this.delegatee = sharedContext;
this.contextHelper = delegatee;
+ this.sessionListener = sessionListener;
+ this.sessionAttributeListener = sessionAttributeListener;
}
@Override
@@ -73,6 +81,18 @@
}
@Override
+ public HttpSessionListener getHttpSessionListener()
+ {
+ return this.sessionListener;
+ }
+
+ @Override
+ public HttpSessionAttributeListener getHttpSessionAttributeListener()
+ {
+ return this.sessionAttributeListener;
+ }
+
+ @Override
public ClassLoader getClassLoader()
{
return this.bundle.adapt(BundleWiring.class).getClassLoader();
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 6e623a3..2319dfe 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
@@ -24,13 +24,17 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
import javax.annotation.Nonnull;
import javax.servlet.ServletContext;
+import javax.servlet.ServletContextListener;
import org.apache.felix.http.base.internal.logger.SystemLogger;
import org.apache.felix.http.base.internal.runtime.AbstractInfo;
import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.runtime.HttpSessionAttributeListenerInfo;
+import org.apache.felix.http.base.internal.runtime.HttpSessionListenerInfo;
import org.apache.felix.http.base.internal.runtime.ResourceInfo;
import org.apache.felix.http.base.internal.runtime.ServletContextAttributeListenerInfo;
import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
@@ -134,8 +138,9 @@
this.httpService.registerContext(handler);
- // context listeners first
+ final Map<ServiceReference<ServletContextListener>, ServletContextListenerInfo> listeners = new TreeMap<ServiceReference<ServletContextListener>, ServletContextListenerInfo>();
final List<WhiteboardServiceInfo<?>> services = new ArrayList<WhiteboardServiceInfo<?>>();
+
for(final Map.Entry<WhiteboardServiceInfo<?>, List<ContextHandler>> entry : this.servicesMap.entrySet())
{
if ( entry.getKey().getContextSelectionFilter().match(handler.getContextInfo().getServiceReference()) )
@@ -143,7 +148,8 @@
entry.getValue().add(handler);
if ( entry.getKey() instanceof ServletContextListenerInfo )
{
- handler.initialized((ServletContextListenerInfo)entry.getKey());
+ final ServletContextListenerInfo info = (ServletContextListenerInfo)entry.getKey();
+ listeners.put(info.getServiceReference(), info);
}
else
{
@@ -151,6 +157,11 @@
}
}
}
+ // context listeners first
+ for(final ServletContextListenerInfo info : listeners.values())
+ {
+ handler.initialized(info);
+ }
// now register services
for(final WhiteboardServiceInfo<?> info : services)
{
@@ -167,7 +178,7 @@
this.httpService.unregisterContext(handler);
// context listeners last
- final List<ServletContextListenerInfo> listeners = new ArrayList<ServletContextListenerInfo>();
+ final Map<ServiceReference<ServletContextListener>, ServletContextListenerInfo> listeners = new TreeMap<ServiceReference<ServletContextListener>, ServletContextListenerInfo>();
final Iterator<Map.Entry<WhiteboardServiceInfo<?>, List<ContextHandler>>> i = this.servicesMap.entrySet().iterator();
while ( i.hasNext() )
{
@@ -176,7 +187,8 @@
{
if ( entry.getKey() instanceof ServletContextListenerInfo )
{
- listeners.add((ServletContextListenerInfo)entry.getKey());
+ final ServletContextListenerInfo info = (ServletContextListenerInfo)entry.getKey();
+ listeners.put(info.getServiceReference(), info);
}
else
{
@@ -187,7 +199,7 @@
}
}
}
- for(final ServletContextListenerInfo info : listeners)
+ for(final ServletContextListenerInfo info : listeners.values())
{
handler.destroyed(info);
}
@@ -373,6 +385,14 @@
{
handler.addListener((ServletContextAttributeListenerInfo)info );
}
+ else if ( info instanceof HttpSessionAttributeListenerInfo )
+ {
+ handler.addListener((HttpSessionAttributeListenerInfo)info );
+ }
+ else if ( info instanceof HttpSessionListenerInfo )
+ {
+ handler.addListener((HttpSessionListenerInfo)info );
+ }
}
/**
@@ -398,6 +418,14 @@
{
handler.removeListener((ServletContextAttributeListenerInfo)info );
}
+ else if ( info instanceof HttpSessionAttributeListenerInfo )
+ {
+ handler.removeListener((HttpSessionAttributeListenerInfo)info );
+ }
+ else if ( info instanceof HttpSessionListenerInfo )
+ {
+ handler.removeListener((HttpSessionListenerInfo)info );
+ }
}
/**
@@ -423,4 +451,20 @@
}
return true;
}
+
+ public ContextHandler getContextHandler(final Long contextId)
+ {
+ synchronized ( this.contextMap )
+ {
+ for(final List<ContextHandler> handlerList : this.contextMap.values())
+ {
+ final ContextHandler h = handlerList.get(0);
+ if ( h.getContextInfo().getServiceId() == contextId )
+ {
+ return h;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
index e5f194c..c285ba9 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
@@ -17,15 +17,19 @@
package org.apache.felix.http.base.internal.whiteboard;
import java.util.ArrayList;
+import java.util.Set;
import javax.annotation.Nonnull;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import org.apache.felix.http.base.internal.context.ExtServletContext;
import org.apache.felix.http.base.internal.handler.FilterHandler;
import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import org.apache.felix.http.base.internal.handler.HttpSessionWrapper;
import org.apache.felix.http.base.internal.handler.ServletHandler;
import org.apache.felix.http.base.internal.runtime.FilterInfo;
import org.apache.felix.http.base.internal.runtime.ResourceInfo;
@@ -221,4 +225,18 @@
{
this.handlerRegistry.remove(contextHandler.getContextInfo());
}
+
+ public void sessionDestroyed(@Nonnull final HttpSession session, final Set<Long> contextIds)
+ {
+ for(final Long contextId : contextIds)
+ {
+ final ContextHandler handler = this.contextManager.getContextHandler(contextId);
+ if ( handler != null )
+ {
+ final ExtServletContext context = handler.getServletContext(this.bundleContext.getBundle());
+ new HttpSessionWrapper(contextId, session, context, true).invalidate();
+ handler.ungetServletContext(this.bundleContext.getBundle());
+ }
+ }
+ }
}