/* * Copyright (C) 2014 Indeed Inc. * * Licensed 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 com.indeed.imhotep.service; import com.google.common.cache.CacheBuilder; import com.indeed.util.core.io.Closeables2; import com.indeed.util.core.reference.SharedReference; import com.indeed.util.varexport.Export; import com.indeed.imhotep.api.ImhotepSession; import org.apache.log4j.Logger; import java.util.HashMap; import java.util.Map; /** * @author jplaisance */ public abstract class AbstractSessionManager<E> implements SessionManager<E> { private static final Logger log = Logger.getLogger(AbstractSessionManager.class); private final Map<String, Session<E>> sessionMap = new HashMap<String, Session<E>>(); private final Map<String, Exception> failureCauseMap = CacheBuilder.newBuilder().maximumSize(200).<String, Exception>build().asMap(); protected void addSession(String sessionId, Session<E> session) { synchronized (sessionMap) { if (sessionMap.containsKey(sessionId)) { throw new IllegalArgumentException("there already exists a session with id "+sessionId); } sessionMap.put(sessionId, session); } } @Override public SharedReference<ImhotepSession> getSession(final String sessionId) { final Session<E> session = internalGetSession(sessionId); return session.imhotepSession.copy(); } @Override public boolean sessionIsValid(final String sessionId) { synchronized (sessionMap) { return sessionMap.containsKey(sessionId); } } @Override public void removeAndCloseIfExists(final String sessionId) { final SharedReference<ImhotepSession> imhotepSession; synchronized (sessionMap) { final Session<E> session = sessionMap.remove(sessionId); if (session == null) { return; } imhotepSession = session.imhotepSession; } Closeables2.closeQuietly(imhotepSession, log); } @Override public void removeAndCloseIfExists(final String sessionId, Exception e) { final SharedReference<ImhotepSession> imhotepSession; synchronized (sessionMap) { final Session<E> session = sessionMap.remove(sessionId); if (session == null) { return; } imhotepSession = session.imhotepSession; } failureCauseMap.put(sessionId, e); Closeables2.closeQuietly(imhotepSession, log); } @Override public void setNumStats(final String sessionId, final int newNumStats) { final Session<E> session = internalGetSession(sessionId); session.numStats = newNumStats; } @Override public int getNumStats(final String sessionId) { final Session<E> session = internalGetSession(sessionId); return session.numStats; } @Override public Map<String, Long> getLastActionTimes() { final Map<String, Session<E>> sessionMap = cloneSessionMap(); final Map<String, Long> ret = new HashMap<String, Long>(); for (final Map.Entry<String, Session<E>> e : sessionMap.entrySet()) { ret.put(e.getKey(), e.getValue().lastActionTime); } return ret; } protected Map<String, Session<E>> cloneSessionMap() { final Map<String, Session<E>> clone; synchronized (sessionMap) { clone = new HashMap<String, Session<E>>(sessionMap); } return clone; } protected Session<E> internalGetSession(final String sessionId) { final Session<E> session; synchronized (sessionMap) { checkSession(sessionId); session = sessionMap.get(sessionId); } session.lastActionTime = System.currentTimeMillis(); return session; } // do not call this method if you do not hold sessionMap's monitor private void checkSession(final String sessionId) { if (!sessionMap.containsKey(sessionId)) { final Exception e = failureCauseMap.get(sessionId); throw new IllegalArgumentException("there does not exist a session with id " + sessionId, e); } } protected static final class Session<E> { protected final SharedReference<ImhotepSession> imhotepSession; protected final E sessionState; protected final String username; protected final String ipAddress; protected final int clientVersion; protected final String dataset; private volatile int numStats; private volatile long lastActionTime; protected Session( ImhotepSession imhotepSession, E sessionState, String username, String ipAddress, int clientVersion, String dataset ) { this.imhotepSession = SharedReference.create(imhotepSession); this.sessionState = sessionState; this.username = username; this.ipAddress = ipAddress; this.clientVersion = clientVersion; this.dataset = dataset; lastActionTime = System.currentTimeMillis(); } } @Export(name = "session-count", doc = "# of open sessions") public int getSessionCount() { synchronized (sessionMap) { return sessionMap.size(); } } }