/** * 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.EndWebBeansListener; import org.apache.openejb.server.httpd.HttpSession; import org.apache.openejb.util.DaemonThreadFactory; import org.apache.openejb.util.Duration; import org.apache.webbeans.config.WebBeansContext; 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) { if (app == null) { return; } final Thread tc = Thread.currentThread(); final ClassLoader tccl = tc.getContextClassLoader(); tc.setContextClassLoader(app.getClassLoader()); try { final Iterator<SessionWrapper> iterator = sessions.values().iterator(); while (iterator.hasNext()) { final SessionWrapper next = iterator.next(); if (next.app == app) { doDestroy(next); iterator.remove(); } } } finally { tc.setContextClassLoader(tccl); } } private void doDestroy(final SessionWrapper next) { HttpSessionEvent event = null; if (next.end != null) { event = new HttpSessionEvent(next.session); next.end.sessionDestroyed(event); next.begin.sessionCreated(event); // just set session thread local } try { next.session.invalidate(); } finally { if (next.begin != null) { next.begin.sessionDestroyed(event); } } } 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(SessionManager.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()) { doDestroy(data); sessions.remove(data.session.getId()); } } } }, 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 begin, final EndWebBeansListener end, final HttpSession session, final WebContext app) { final SessionWrapper wrapper = new SessionWrapper(begin, end, session, app); final SessionWrapper existing = sessions.putIfAbsent(session.getId(), wrapper); if (existing == null && es == null) { synchronized (this) { if (es == null) { initEviction(); } } } return existing == null ? wrapper : existing; } public static class SessionWrapper extends HttpSessionEvent { public final BeginWebBeansListener begin; public final EndWebBeansListener end; public final HttpSession session; public final WebContext app; public SessionWrapper(final BeginWebBeansListener begin, final EndWebBeansListener end, final HttpSession session, final WebContext app) { super(session); this.begin = begin; this.end = end; this.session = session; this.app = app; } } }