/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.undertow.session; import java.util.AbstractMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import io.undertow.server.session.Session; import org.jboss.metadata.web.jboss.JBossWebMetaData; import org.jboss.metadata.web.spec.SessionConfigMetaData; /** * @author Paul Ferraro */ public abstract class AbstractSessionManager implements SessionManager { /** Maximum of active sessions allowed. -1 is unlimited. */ protected int maxActiveAllowed = -1; /** Has this component been started yet? */ protected volatile boolean started = false; /** Are we allowing backgroundProcess() to execute? We use an object so stop() can lock on it to wait for */ // protected AtomicBoolean backgroundProcessAllowed = new AtomicBoolean(); private final ReplicationStatistics stats = new ReplicationStatistics(); /** Number of sessions created by this manager */ protected final AtomicInteger createdCounter = new AtomicInteger(); /** number of sessions rejected because the number active sessions exceeds maxActive */ protected final AtomicInteger rejectedCounter = new AtomicInteger(); /** Number of active sessions */ protected final AtomicInteger localActiveCounter = new AtomicInteger(); /** Maximum number of concurrently locally active sessions */ protected final AtomicInteger maxLocalActiveCounter = new AtomicInteger(); /** Maximum number of active sessions seen so far */ protected final AtomicInteger maxActiveCounter = new AtomicInteger(); /** Number of sessions that have been active locally that are now expired. */ protected final AtomicInteger expiredCounter = new AtomicInteger(); /** Number of ms since last call to reset() */ protected long timeSinceLastReset = 0; /** Cumulative time spent in backgroundProcess */ protected final AtomicLong processingTime = new AtomicLong(); /** Maximum time in ms a now expired session has been alive */ protected final AtomicInteger maxAliveTime = new AtomicInteger(); /** Average time in ms a now expired session has been alive */ protected final AtomicInteger averageAliveTime = new AtomicInteger(); /** Number of times our session id generator has generated an id that matches an existing session. */ protected final AtomicInteger duplicates = new AtomicInteger(); /** * The default maximum inactive interval for Sessions created by this Manager. */ protected volatile int maxInactiveTime = 30 * 60; protected volatile int maxActive = 0; protected final Map<String, Session> sessions = new ConcurrentHashMap<String, Session>(); protected AbstractSessionManager(JBossWebMetaData metaData) { //this.processExpiresFrequency = 1; Integer maxActiveSessions = metaData.getMaxActiveSessions(); if (maxActiveSessions != null) { this.setMaxActiveAllowed(maxActiveSessions.intValue()); } SessionConfigMetaData config = metaData.getSessionConfig(); if (config != null) { // Convert session timeout (minutes) to max inactive interval (seconds) maxInactiveTime = config.getSessionTimeout() * 60; } } public synchronized void start() { this.started = true; } public void stop() { // Validate and update our current component state if (!this.started) return; this.started = false; // Block for any ongoing backgroundProcess, then disable synchronized (this) { resetStats(); } } public void processExpires() { synchronized (this) { if (this.started) { long start = System.currentTimeMillis(); processExpirationPassivation(); processingTime.addAndGet(System.currentTimeMillis() - start); } } } protected abstract void processExpirationPassivation(); public int getRejectedSessions() { return this.rejectedCounter.get(); } public void setRejectedSessions(int rejectedSessions) { this.rejectedCounter.set(rejectedSessions); } public ReplicationStatistics getReplicationStatistics() { return this.stats; } public void resetStats() { stats.resetStats(); maxActiveCounter.set(localActiveCounter.get()); rejectedCounter.set(0); createdCounter.set(0); expiredCounter.set(0); processingTime.set(0); maxAliveTime.set(0); averageAliveTime.set(0); duplicates.set(0); timeSinceLastReset = System.currentTimeMillis(); } public long getTimeSinceLastReset() { return this.timeSinceLastReset; } public long getActiveSessionCount() { return calcActiveSessions(); } public long getLocalActiveSessionCount() { return this.localActiveCounter.get(); } public long getRejectedSessionCount() { return this.rejectedCounter.get(); } public long getCreatedSessionCount() { return this.createdCounter.get(); } public long getExpiredSessionCount() { return this.expiredCounter.get(); } public long getMaxActiveSessionCount() { return this.maxActiveCounter.get(); } public long getMaxLocalActiveSessionCount() { return this.maxLocalActiveCounter.get(); } public int getMaxActiveAllowed() { return this.maxActiveAllowed; } public void setMaxActiveAllowed(int max) { this.maxActiveAllowed = max; } public int getMaxActiveSessions() { return this.maxActive; } public Map.Entry<String, String> parse(String sessionId) { String realId = sessionId; String jvmRoute = null; int index = sessionId.indexOf('.', 0); if (index > 0) { realId = sessionId.substring(0, index); if (index < sessionId.length() - 1) { jvmRoute = sessionId.substring(index + 1); } } return new AbstractMap.SimpleImmutableEntry<String, String>(realId, jvmRoute); } public String createSessionId(String realId, String jvmRoute) { return (jvmRoute != null) ? realId + "." + jvmRoute : realId; } /** * Updates statistics to reflect that a session with a given "alive time" has been expired. * * @param sessionAliveTime number of ms from when the session was created to when it was expired. */ protected void sessionExpired(int sessionAliveTime) { int current = maxAliveTime.get(); while (sessionAliveTime > current) { if (maxAliveTime.compareAndSet(current, sessionAliveTime)) break; else current = maxAliveTime.get(); } expiredCounter.incrementAndGet(); int newAverage; do { int expCount = expiredCounter.get(); current = averageAliveTime.get(); newAverage = ((current * (expCount - 1)) + sessionAliveTime) / expCount; } while (averageAliveTime.compareAndSet(current, newAverage) == false); } /** * Calculates the number of active sessions, and updates the max # of local active sessions and max # of sessions. * <p> * Call this method when a new session is added or when an accurate count of active sessions is needed. * </p> * * @return the size of the sessions map + the size of the unloaded sessions map - the count of passivated sessions */ protected int calcActiveSessions() { localActiveCounter.set(sessions.size()); int active = localActiveCounter.get(); int maxLocal = maxLocalActiveCounter.get(); while (active > maxLocal) { if (!maxLocalActiveCounter.compareAndSet(maxLocal, active)) { maxLocal = maxLocalActiveCounter.get(); } } int count = getTotalActiveSessions(); int max = maxActiveCounter.get(); while (count > max) { if (!maxActiveCounter.compareAndSet(max, count)) { max = maxActiveCounter.get(); // Something changed, so reset our count count = getTotalActiveSessions(); } } return count; } /** Get the total number of active sessions */ protected abstract int getTotalActiveSessions(); }