You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by st...@apache.org on 2015/05/17 13:20:56 UTC

[10/37] tomee git commit: TOMEE-1571 destroying http session of openejb-http layer when undeploying applications with openejb embedded arquillian adapter

TOMEE-1571 destroying http session of openejb-http layer when undeploying applications with openejb embedded arquillian adapter


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/95ea7d5b
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/95ea7d5b
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/95ea7d5b

Branch: refs/heads/master
Commit: 95ea7d5be3a8da495a71003ba9bd61bf5bcad27b
Parents: 065fc36
Author: Romain Manni-Bucau <rm...@apache.org>
Authored: Sun May 3 22:20:13 2015 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Mon May 4 19:32:04 2015 +0200

----------------------------------------------------------------------
 .../openejb/OpenEJBDeployableContainer.java     |  30 ++++-
 .../http/WebArchiveResourceProvider.java        |  16 +++
 .../arquillian/openejb/SessionDestroyTest.java  | 113 ++++++++++++++++
 .../openejb/server/httpd/HttpRequestImpl.java   | 126 +++++-------------
 .../openejb/server/httpd/HttpResponseImpl.java  |   3 +-
 .../openejb/server/httpd/HttpSessionImpl.java   |  26 ++--
 .../server/httpd/OpenEJBHttpRegistry.java       |   2 +
 .../openejb/server/httpd/OpenEJBHttpServer.java |   9 +-
 .../server/httpd/session/SessionManager.java    | 132 +++++++++++++++++++
 .../httpd/HttpResponseImplSessionTest.java      |  16 +--
 10 files changed, 346 insertions(+), 127 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java b/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java
index 90d86b1..44915b8 100644
--- a/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java
+++ b/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java
@@ -37,8 +37,10 @@ import org.apache.openejb.config.DeploymentFilterable;
 import org.apache.openejb.config.WebModule;
 import org.apache.openejb.core.LocalInitialContext;
 import org.apache.openejb.core.LocalInitialContextFactory;
+import org.apache.openejb.core.WebContext;
 import org.apache.openejb.loader.IO;
 import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.server.httpd.session.SessionManager;
 import org.apache.openejb.web.LightweightWebAppBuilder;
 import org.apache.webbeans.web.lifecycle.test.MockHttpSession;
 import org.apache.webbeans.web.lifecycle.test.MockServletContext;
@@ -58,11 +60,6 @@ import org.jboss.arquillian.test.spi.annotation.SuiteScoped;
 import org.jboss.shrinkwrap.api.Archive;
 import org.jboss.shrinkwrap.descriptor.api.Descriptor;
 
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpSession;
 import java.io.ByteArrayInputStream;
 import java.io.Closeable;
 import java.io.IOException;
@@ -75,6 +72,11 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
 
 import static org.apache.openejb.cdi.ScopeHelper.startContexts;
 import static org.apache.openejb.cdi.ScopeHelper.stopContexts;
@@ -295,7 +297,8 @@ public class OpenEJBDeployableContainer implements DeployableContainer<OpenEJBCo
                 final AppInfo appInfo = configurationFactory.configureApplication(module);
 
                 final WebAppBuilder webAppBuilder = SystemInstance.get().getComponent(WebAppBuilder.class);
-                if (webAppBuilder != null && LightweightWebAppBuilder.class.isInstance(webAppBuilder)) {
+                final boolean isEmbeddedWebAppBuilder = webAppBuilder != null && LightweightWebAppBuilder.class.isInstance(webAppBuilder);
+                if (isEmbeddedWebAppBuilder) {
                     // for now we keep the same classloader, open to discussion if we should recreate it, not sure it does worth it
                     final LightweightWebAppBuilder lightweightWebAppBuilder = LightweightWebAppBuilder.class.cast(webAppBuilder);
                     for (final WebModule w : module.getWebModules()) {
@@ -310,6 +313,21 @@ public class OpenEJBDeployableContainer implements DeployableContainer<OpenEJBCo
                     }
                 }
                 final AppContext appCtx = assembler.createApplication(appInfo, module.getClassLoader());
+                if (isEmbeddedWebAppBuilder && PROPERTIES.containsKey(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE) && !appCtx.getWebContexts().isEmpty()) {
+                    cls.add(new Closeable() {
+                        @Override
+                        public void close() throws IOException {
+                            try {
+                                final SessionManager sessionManager = SystemInstance.get().getComponent(SessionManager.class);
+                                for (final WebContext web : appCtx.getWebContexts()) {
+                                    sessionManager.destroy(web);
+                                }
+                            } catch (final Throwable e) {
+                                // no-op
+                            }
+                        }
+                    });
+                }
 
                 final ServletContext appServletContext = new MockServletContext();
                 final HttpSession appSession = new MockHttpSession();

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/http/WebArchiveResourceProvider.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/http/WebArchiveResourceProvider.java b/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/http/WebArchiveResourceProvider.java
index 2f1f731..5fd0b04 100644
--- a/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/http/WebArchiveResourceProvider.java
+++ b/arquillian/arquillian-openejb-embedded-5/src/main/java/org/apache/openejb/arquillian/openejb/http/WebArchiveResourceProvider.java
@@ -1,3 +1,19 @@
+/*
+ * 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.openejb.arquillian.openejb.http;
 
 import org.apache.openejb.arquillian.openejb.SWClassLoader;

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/SessionDestroyTest.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/SessionDestroyTest.java b/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/SessionDestroyTest.java
new file mode 100644
index 0000000..fbae01e
--- /dev/null
+++ b/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/SessionDestroyTest.java
@@ -0,0 +1,113 @@
+/**
+ * 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.openejb.arquillian.openejb;
+
+import org.apache.openejb.loader.IO;
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.junit.InSequence;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.net.URL;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebListener;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(Arquillian.class)
+public class SessionDestroyTest {
+    @Deployment(name = "app", managed = false, testable = false)
+    public static Archive<?> app() {
+        return ShrinkWrap.create(WebArchive.class).addClasses(SessionTestManager.class, SessionListener.class);
+    }
+
+    @ArquillianResource
+    private Deployer deployer;
+
+    private static String id;
+
+    @Test
+    @InSequence(1)
+    public void deploy() {
+        reset();
+        deployer.deploy("app");
+    }
+
+    @Test
+    @InSequence(2)
+    @OperateOnDeployment("app")
+    public void doTest(@ArquillianResource final URL url) throws IOException {
+        id = IO.slurp(new URL(url.toExternalForm() + "create"));
+        assertNotNull(SessionListener.created);
+        assertEquals(id, SessionListener.created);
+    }
+
+    @Test
+    @InSequence(3)
+    public void undeployAndAsserts() {
+        deployer.undeploy("app");
+        assertNotNull(SessionListener.destroyed);
+        assertEquals(id, SessionListener.destroyed);
+        reset();
+    }
+
+    private void reset() {
+        SessionListener.destroyed = null;
+        SessionListener.created = null;
+        id = null;
+    }
+
+    @WebServlet("/create")
+    public static class SessionTestManager extends HttpServlet {
+        @Override
+        protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+            req.getSession().setAttribute("test", "ok");
+            resp.getWriter().write(req.getSession().getId());
+        }
+    }
+
+    @WebListener
+    public static class SessionListener implements HttpSessionListener {
+        private static String created;
+        private static String destroyed;
+
+        @Override
+        public void sessionCreated(final HttpSessionEvent httpSessionEvent) {
+            created = httpSessionEvent.getSession().getId();
+        }
+
+        @Override
+        public void sessionDestroyed(final HttpSessionEvent httpSessionEvent) {
+            destroyed = httpSessionEvent.getSession().getId();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java
----------------------------------------------------------------------
diff --git a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java
index 6006178..eaf420f 100644
--- a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java
+++ b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpRequestImpl.java
@@ -19,30 +19,13 @@ package org.apache.openejb.server.httpd;
 import org.apache.openejb.assembler.classic.AppInfo;
 import org.apache.openejb.assembler.classic.Assembler;
 import org.apache.openejb.assembler.classic.WebAppInfo;
+import org.apache.openejb.core.WebContext;
 import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.server.httpd.session.SessionManager;
 import org.apache.openejb.spi.SecurityService;
 import org.apache.openejb.util.ArrayEnumeration;
-import org.apache.openejb.util.DaemonThreadFactory;
-import org.apache.openejb.util.Duration;
 import org.apache.openejb.util.Logger;
 
-import javax.security.auth.login.LoginException;
-import javax.servlet.AsyncContext;
-import javax.servlet.DispatcherType;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletRequestEvent;
-import javax.servlet.ServletRequestListener;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpUpgradeHandler;
-import javax.servlet.http.Part;
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInput;
@@ -67,11 +50,23 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.StringTokenizer;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
+import javax.security.auth.login.LoginException;
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpUpgradeHandler;
+import javax.servlet.http.Part;
 
 import static java.util.Collections.singletonList;
 
@@ -83,11 +78,6 @@ public class HttpRequestImpl implements HttpRequest {
     private static final String FORM_URL_ENCODED = "application/x-www-form-urlencoded";
     private static final String TRANSFER_ENCODING = "Transfer-Encoding";
     private static final String CHUNKED = "chunked";
-    protected static final String EJBSESSIONID = "EJBSESSIONID";
-    protected static final String JSESSIONID = "JSESSIONID";
-
-    // note: no eviction so invalidate has to be called properly
-    private static final ConcurrentMap<String, RequestSession> SESSIONS = new ConcurrentHashMap<>();
 
     public static final Class<?>[] SERVLET_CONTEXT_INTERFACES = new Class<?>[]{ServletContext.class};
     public static final InvocationHandler SERVLET_CONTEXT_HANDLER = new InvocationHandler() {
@@ -97,46 +87,9 @@ public class HttpRequestImpl implements HttpRequest {
         }
     };
 
-    private static volatile ScheduledExecutorService es;
-
-    public static void destroyEviction() {
-        if (es == null) {
-            return;
-        }
-        es.shutdownNow();
-        for (RequestSession requestSession : SESSIONS.values()) {
-            requestSession.session.invalidate();
-        }
-        SESSIONS.clear();
-    }
-
-    public static void initEviction() {
-        if (!"true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.http.eviction", "true"))) {
-            return;
-        }
-        final Duration duration = new Duration(SystemInstance.get().getProperty("openejb.http.eviction.duration", "1 minute"));
-        es = Executors.newScheduledThreadPool(1, new DaemonThreadFactory(HttpRequestImpl.class));
-        es.scheduleWithFixedDelay(new Runnable() {
-            @Override
-            public void run() {
-                for (final RequestSession data : new ArrayList<>(SESSIONS.values())) {
-                    final HttpSession session = data.session;
-                    if (session.getMaxInactiveInterval() > 0
-                            && session.getLastAccessedTime() + TimeUnit.SECONDS.toMillis(session.getMaxInactiveInterval()) < System.currentTimeMillis()) {
-                        SESSIONS.remove(session.getId());
-                        session.invalidate();
-
-                        if (data.request != null && data.request.begin != null) {
-                            data.request.begin.sessionDestroyed(new HttpSessionEvent(session));
-                        }
-                    }
-                }
-            }
-        }, duration.getTime(), duration.getTime(), duration.getUnit());
-    }
-
     private EndWebBeansListener end;
     private BeginWebBeansListener begin;
+    private WebContext application;
 
     /**
      * 5.1.1    Method
@@ -441,11 +394,13 @@ public class HttpRequestImpl implements HttpRequest {
                 for (String c : cookies) {
                     final String current = c.trim();
                     if (current.startsWith("EJBSESSIONID=")) {
-                        final RequestSession requestSession = SESSIONS.get(current.substring("EJBSESSIONID=".length()));
-                        session = requestSession == null ? null : requestSession.session;
+                        final SessionManager.SessionWrapper sessionWrapper =
+                                SystemInstance.get().getComponent(SessionManager.class).findSession(current.substring("EJBSESSIONID=".length()));
+                        session = sessionWrapper == null ? null : sessionWrapper.session;
                     } else if (current.startsWith("JSESSIONID=")) {
-                        final RequestSession requestSession = SESSIONS.get(current.substring("JSESSIONID=".length()));
-                        session = requestSession == null ? null : requestSession.session;
+                        final SessionManager.SessionWrapper sessionWrapper =
+                                SystemInstance.get().getComponent(SessionManager.class).findSession(current.substring("JSESSIONID=".length()));
+                        session = sessionWrapper == null ? null : sessionWrapper.session;
                     }
                 }
             }
@@ -932,7 +887,7 @@ public class HttpRequestImpl implements HttpRequest {
                 }
             }
 
-            final HttpSessionImpl impl = new HttpSessionImpl(SESSIONS, contextPath, timeout);
+            final HttpSessionImpl impl = new HttpSessionImpl(contextPath, timeout);
             session = impl;
             if (begin != null) {
                 begin.sessionCreated(new HttpSessionEvent(session));
@@ -940,17 +895,10 @@ public class HttpRequestImpl implements HttpRequest {
             }
             impl.callListeners(); // can call req.getSession() so do it after affectation + do it after cdi init
 
-            final RequestSession previous = SESSIONS.putIfAbsent(session.getId(), new RequestSession(this, session));
+            final SessionManager sessionManager = SystemInstance.get().getComponent(SessionManager.class);
+            final SessionManager.SessionWrapper previous = sessionManager.newSession(begin, end, session, application);
             if (previous != null) {
                 session = previous.session;
-            } else {
-                if (es == null) {
-                    synchronized (HttpRequestImpl.class) {
-                        if (es == null) {
-                            initEviction();
-                        }
-                    }
-                }
             }
         }
         return session;
@@ -1222,6 +1170,10 @@ public class HttpRequestImpl implements HttpRequest {
         }
     }
 
+    public void setApplication(final WebContext app) {
+        this.application = app;
+    }
+
     public void setBeginListener(final BeginWebBeansListener begin) {
         if (this.begin == null) {
             this.begin = begin;
@@ -1270,7 +1222,6 @@ public class HttpRequestImpl implements HttpRequest {
 
         @Override
         public void invalidate() {
-            SESSIONS.remove(session.getId());
             try {
                 super.invalidate();
             } finally {
@@ -1317,15 +1268,4 @@ public class HttpRequestImpl implements HttpRequest {
             // not yet supported: TODO: fake response write in ByteArrayOutputStream and then call HttpListenerRegistry and write it back
         }
     }
-
-    private static class RequestSession extends HttpSessionEvent {
-        private final HttpRequestImpl request;
-        private final HttpSession session;
-
-        public RequestSession(final HttpRequestImpl request, final HttpSession session) {
-            super(session);
-            this.request = request;
-            this.session = session;
-        }
-    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpResponseImpl.java
----------------------------------------------------------------------
diff --git a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpResponseImpl.java b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpResponseImpl.java
index 9692e28..180e3e2 100644
--- a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpResponseImpl.java
+++ b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpResponseImpl.java
@@ -17,6 +17,7 @@
 package org.apache.openejb.server.httpd;
 
 import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.server.httpd.session.SessionManager;
 import org.apache.openejb.util.LogCategory;
 import org.apache.openejb.util.Logger;
 import org.apache.openejb.util.OpenEjbVersion;
@@ -483,7 +484,7 @@ public class HttpResponseImpl implements HttpResponse {
             return;
         }
 
-        headers.put(HttpRequest.HEADER_SET_COOKIE, HttpRequestImpl.EJBSESSIONID + '=' + session.getId() + "; Path=/");
+        headers.put(HttpRequest.HEADER_SET_COOKIE, SessionManager.EJBSESSIONID + '=' + session.getId() + "; Path=/");
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpSessionImpl.java
----------------------------------------------------------------------
diff --git a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpSessionImpl.java b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpSessionImpl.java
index cd0a394..657a394 100644
--- a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpSessionImpl.java
+++ b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpSessionImpl.java
@@ -18,11 +18,8 @@ package org.apache.openejb.server.httpd;
 
 import org.apache.openejb.client.ArrayEnumeration;
 import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.server.httpd.session.SessionManager;
 
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpSessionContext;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpSessionListener;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -31,19 +28,20 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.UUID;
-import java.util.concurrent.ConcurrentMap;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
 
 public class HttpSessionImpl implements HttpSession {
     private Collection<HttpSessionListener> listeners;
     private String sessionId = UUID.randomUUID().toString();
     private Map<String, Object> attributes = new HashMap<String, Object>();
-    private final ConcurrentMap<String, ? extends HttpSessionEvent> mapToClean;
     private final long created = System.currentTimeMillis();
     private volatile long timeout;
     private volatile long lastAccessed = created;
 
-    public HttpSessionImpl(final ConcurrentMap<String, ? extends HttpSessionEvent> sessions, final String contextPath, final long timeout) {
-        this.mapToClean = sessions;
+    public HttpSessionImpl(final String contextPath, final long timeout) {
         this.timeout = timeout;
         if (contextPath == null) {
             return;
@@ -62,7 +60,7 @@ public class HttpSessionImpl implements HttpSession {
     }
 
     public HttpSessionImpl() {
-        this(null, null, 30000);
+        this(null, 30000);
     }
 
     public void newSessionId() {
@@ -105,8 +103,9 @@ public class HttpSessionImpl implements HttpSession {
         }
 
         attributes.clear();
-        if (mapToClean != null) {
-            mapToClean.remove(sessionId);
+        final SessionManager sessionManager = SystemInstance.get().getComponent(SessionManager.class);
+        if (sessionManager != null) {
+            sessionManager.removeSession(sessionId);
         }
     }
 
@@ -186,16 +185,17 @@ public class HttpSessionImpl implements HttpSession {
     @Override
     public HttpSessionContext getSessionContext() {
         touch();
+        final SessionManager component = SystemInstance.get().getComponent(SessionManager.class);
         return new HttpSessionContext() {
             @Override
             public javax.servlet.http.HttpSession getSession(final String sessionId) {
-                final HttpSessionEvent event = mapToClean.get(sessionId);
+                final HttpSessionEvent event = component.findSession(sessionId);
                 return event == null ? null : event.getSession();
             }
 
             @Override
             public Enumeration<String> getIds() {
-                return Collections.enumeration(mapToClean.keySet());
+                return Collections.enumeration(component.findSessionIds());
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpRegistry.java
----------------------------------------------------------------------
diff --git a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpRegistry.java b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpRegistry.java
index 3a61442..6dd8d9e 100644
--- a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpRegistry.java
+++ b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpRegistry.java
@@ -113,6 +113,8 @@ public class OpenEJBHttpRegistry {
                     final HttpRequestImpl httpRequest = HttpRequestImpl.class.cast(request);
                     final WebContext web = findWebContext(request.getURI() == null ? request.getContextPath() : request.getURI().getPath());
                     if (web != null) {
+                        httpRequest.setApplication(web);
+
                         if (web.getClassLoader() != null) {
                             thread.setContextClassLoader(web.getClassLoader());
                         } else if (web.getAppContext().getClassLoader() != null) {

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpServer.java
----------------------------------------------------------------------
diff --git a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpServer.java b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpServer.java
index 33f358c..65e8c66 100644
--- a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpServer.java
+++ b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/OpenEJBHttpServer.java
@@ -22,6 +22,7 @@ import org.apache.openejb.loader.Options;
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.server.ServiceException;
 import org.apache.openejb.server.context.RequestInfos;
+import org.apache.openejb.server.httpd.session.SessionManager;
 import org.apache.openejb.server.stream.CountingInputStream;
 import org.apache.openejb.server.stream.CountingOutputStream;
 import org.apache.openejb.util.LogCategory;
@@ -76,6 +77,9 @@ public class OpenEJBHttpServer implements HttpServer {
 
     public OpenEJBHttpServer(final HttpListener listener) {
         this.listener = new OpenEJBHttpRegistry.ClassLoaderHttpListener(listener, ParentClassLoaderFinder.Helper.get());
+        if (SystemInstance.get().getComponent(SessionManager.class) == null) {
+            SystemInstance.get().setComponent(SessionManager.class, new SessionManager());
+        }
     }
 
     public static boolean isTextXml(final Map<String, String> headers) {
@@ -170,7 +174,10 @@ public class OpenEJBHttpServer implements HttpServer {
     @Override
     public void stop() throws ServiceException {
         OpenEJBAsyncContext.destroy();
-        HttpRequestImpl.destroyEviction();
+        final SessionManager component = SystemInstance.get().getComponent(SessionManager.class);
+        if (component != null) {
+            component.destroy();
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/session/SessionManager.java
----------------------------------------------------------------------
diff --git a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/session/SessionManager.java b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/session/SessionManager.java
new file mode 100644
index 0000000..ad455b1
--- /dev/null
+++ b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/session/SessionManager.java
@@ -0,0 +1,132 @@
+/**
+ * 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.openejb.server.httpd.session;
+
+import org.apache.openejb.core.WebContext;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.server.httpd.BeginWebBeansListener;
+import org.apache.openejb.server.httpd.HttpRequestImpl;
+import org.apache.openejb.server.httpd.HttpSession;
+import org.apache.openejb.util.DaemonThreadFactory;
+import org.apache.openejb.util.Duration;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.http.HttpSessionEvent;
+
+public class SessionManager {
+    public static final String EJBSESSIONID = "EJBSESSIONID";
+    public static final String JSESSIONID = "JSESSIONID";
+
+    private final ConcurrentMap<String, SessionWrapper> sessions = new ConcurrentHashMap<>();
+
+    private static volatile ScheduledExecutorService es;
+
+    public void destroy(final WebContext app) {
+        final Iterator<SessionWrapper> iterator = sessions.values().iterator();
+        while (iterator.hasNext()) {
+            final SessionWrapper next = iterator.next();
+            if (next.app == app) {
+                next.session.invalidate();
+                iterator.remove();
+            }
+        }
+    }
+
+    public void destroy() {
+        if (es == null) {
+            return;
+        }
+        es.shutdownNow();
+        for (final SessionWrapper rs : sessions.values()) {
+            rs.session.invalidate();
+        }
+        sessions.clear();
+    }
+
+    public void initEviction() {
+        if (!"true".equalsIgnoreCase(SystemInstance.get().getProperty("openejb.http.eviction", "true"))) {
+            return;
+        }
+        final Duration duration = new Duration(SystemInstance.get().getProperty("openejb.http.eviction.duration", "1 minute"));
+        es = Executors.newScheduledThreadPool(1, new DaemonThreadFactory(HttpRequestImpl.class));
+        es.scheduleWithFixedDelay(new Runnable() {
+            @Override
+            public void run() {
+                for (final SessionWrapper data : new ArrayList<>(sessions.values())) {
+                    final HttpSession session = data.session;
+                    if (session.getMaxInactiveInterval() > 0
+                            && session.getLastAccessedTime() + TimeUnit.SECONDS.toMillis(session.getMaxInactiveInterval()) < System.currentTimeMillis()) {
+                        sessions.remove(session.getId());
+                        session.invalidate();
+
+                        if (data.listener != null) {
+                            data.listener.sessionDestroyed(new HttpSessionEvent(session));
+                        }
+                    }
+                }
+            }
+        }, duration.getTime(), duration.getTime(), duration.getUnit());
+    }
+
+    public SessionWrapper findSession(final String id) {
+        return sessions.get(id);
+    }
+
+    public void removeSession(final String sessionId) {
+        sessions.remove(sessionId);
+    }
+
+    public Collection<String> findSessionIds() {
+        return sessions.keySet();
+    }
+
+    public int size() {
+        return sessions.size();
+    }
+
+    public SessionWrapper newSession(final BeginWebBeansListener listener, final HttpSession session, final WebContext app) {
+        final SessionWrapper existing = sessions.putIfAbsent(session.getId(), new SessionWrapper(listener, session, app));
+        if (existing == null && es == null) {
+            synchronized (this) {
+                if (es == null) {
+                    initEviction();
+                }
+            }
+        }
+        return existing;
+    }
+
+    public static class SessionWrapper extends HttpSessionEvent {
+        public final BeginWebBeansListener listener;
+        public final HttpSession session;
+        public final WebContext app;
+
+        public SessionWrapper(final BeginWebBeansListener listener, final HttpSession session, final WebContext app) {
+            super(session);
+            this.listener = listener;
+            this.session = session;
+            this.app = app;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/95ea7d5b/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpResponseImplSessionTest.java
----------------------------------------------------------------------
diff --git a/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpResponseImplSessionTest.java b/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpResponseImplSessionTest.java
index 595c9be..b43d130 100644
--- a/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpResponseImplSessionTest.java
+++ b/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/HttpResponseImplSessionTest.java
@@ -21,41 +21,31 @@ import org.apache.openejb.core.CoreContainerSystem;
 import org.apache.openejb.core.ivm.naming.IvmJndiFactory;
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.server.ServiceException;
+import org.apache.openejb.server.httpd.session.SessionManager;
 import org.apache.openejb.spi.ContainerSystem;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
-import javax.servlet.http.HttpSession;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.lang.reflect.Field;
 import java.net.Socket;
-import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.atomic.AtomicReference;
+import javax.servlet.http.HttpSession;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 public class HttpResponseImplSessionTest {
-    private static Field sessions;
-
     private OpenEJBHttpEjbServer server;
 
-    @BeforeClass
-    public static void findSessionsField() throws NoSuchFieldException {
-        sessions = HttpRequestImpl.class.getDeclaredField("SESSIONS");
-        sessions.setAccessible(true);
-    }
-
     private static int numberOfSessions() throws IllegalAccessException {
-        return Map.class.cast(sessions.get(null)).size();
+        return SystemInstance.get().getComponent(SessionManager.class).size();
     }
 
     @Before