/* ********************************************************** * Copyright 2010 VMware, Inc. All rights reserved. -- VMware Confidential * **********************************************************/ package com.emc.storageos.vasa.util; import java.util.HashSet; import java.util.LinkedList; import java.util.Random; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.locks.ReentrantLock; import org.apache.log4j.Logger; import com.emc.storageos.vasa.SOSManager; import com.vmware.vim.vasa._1_0.StorageFault; import com.vmware.vim.vasa._1_0.data.xsd.UsageContext; /** * Track the indiviudal Sessions/Contexts connections to SampleVP */ public class SessionContext { public static long DEFAULT_SESSION_TIMEOUT = 300; // seconds public static String INVALID_SESSION_ID = "0"; private static Random rand = new Random(); private static Logger log = Logger.getLogger(SessionContext.class); // // the assignment to sessionContextList is considered atomic and does // not requiere that sessionContextListLock be held private static LinkedList<SessionContext> sessionContextList = new LinkedList<SessionContext>(); private static Set<String> sessionIdList = new HashSet<String>(); // // sessionContextListLock must be held while sessionContextList is // being manipulated. private static ReentrantLock sessionContextListLock = new ReentrantLock(); private static int MAX_SESSION_ID = 1000000; private String id; private String clientAddress; private UsageContext context; private Timer timer; private SOSManager sosManager; /* * If the SessionId has not been used in more than * UsageContext.timeoutInSeconds, then void the Session and free the * resources. */ class SessionTimeoutTask extends TimerTask { public void run() { removeFromList(); } } @Override @SuppressWarnings("squid:S1206") // "equals(Object obj)" and "hashCode()" should be overridden in pairs public boolean equals(Object o) { SessionContext sc = (SessionContext) o; return this.getSessionId().equals(sc.getSessionId()); } /* * Cancel any existing timer task and start a new one. */ private boolean restartTimer() { if (this.timer != null) { this.timer.cancel(); } this.timer = new Timer(true); try { long timeout = this.context.getSessionTimeoutInSeconds(); if (timeout <= 0) { timeout = DEFAULT_SESSION_TIMEOUT; } this.timer.schedule(new SessionTimeoutTask(), timeout * 1000); return true; } catch (Exception e) { log.error("Could not restart session timer for SessionId " + id + " e: " + e); return false; } } /** * * @param sessionId * @param alloateIfNotFound * @param uc * @param clientAddress */ private static SessionContext lookupSessionBySessionId(String sessionId, boolean allocateIfNotFound, UsageContext uc, String clientAddress) throws StorageFault { if ((sessionContextList != null) && (sessionId != null) && (!sessionId.equals(INVALID_SESSION_ID))) { sessionContextListLock.lock(); try { for (int i = 0; i < sessionContextList.size(); i++) { SessionContext sc = (SessionContext) sessionContextList .get(i); if ((sc != null) && (sc.getSessionId().equals(sessionId))) { sc.restartTimer(); return sc; } if (sc == null) { log.trace("SessionContext instance " + i + " is null."); throw FaultUtil .StorageFault("Session List may be corrupted."); } } } finally { sessionContextListLock.unlock(); } } if (allocateIfNotFound) { return new SessionContext(clientAddress, uc); } return null; } private static String generateRandomId() { /* * pick a random integer evenly distributed between 0 and MAX_SESSION_ID * -1 */ int nextRandomInt = rand.nextInt(MAX_SESSION_ID); /* * Add 1 to the random number since 0 is not a valid session id; */ return String.valueOf(nextRandomInt + 1); } private static String generateUniqueRandomId() { String randomId = generateRandomId(); try { while (lookupSessionContextBySessionId(randomId) != null) { /* * If this id is already being used, get a different id. */ randomId = generateRandomId(); } } catch (Exception e) { // ignore the failure log.error("generateUniqueRandomId(): Exception : " + e); } return randomId; } /* * SessionContext class Constructor * * @param ca * * @param us */ private SessionContext(String ca, UsageContext uc) { id = generateUniqueRandomId(); log.trace("SessionId: " + id + "added"); clientAddress = ca; context = uc; timer = null; sessionContextListLock.lock(); try { sessionContextList.add(this); sessionIdList.add(id); } finally { sessionContextListLock.unlock(); } restartTimer(); } private static void removeFromList(SessionContext sc) { if ((sc != null) && (sc.timer != null)) { sc.timer.cancel(); } if ((sessionContextList != null) && (sc != null)) { boolean removed = false; String id = sc.getSessionId(); sessionContextListLock.lock(); try { removed = sessionContextList.remove(sc); } finally { sessionContextListLock.unlock(); } if (removed) { log.trace("Session " + id + " removed."); } else { log.trace("Session " + id + " could not be removed. Not found in list."); } } } private void removeFromList() { log.trace("Session " + this.getSessionId() + " timed out."); removeFromList(this); } /* public methods */ public String getSessionId() { return id; } public String getClientAddress() { return clientAddress; } public UsageContext getUsageContext() { return context; } public SOSManager getSosManager() { return sosManager; } public void setSosManager(SOSManager sosManager) { this.sosManager = sosManager; } /* * Lookup SessionContext with SessionId. If a SessionContext exists, remove * it. Create new SessionContext. * * @param sessionId * * @param uc * * @param clientAddress */ public static SessionContext createSession(UsageContext uc, String clientAddress) throws StorageFault { /* * Create new SessionContext */ return lookupSessionBySessionId(INVALID_SESSION_ID, true, uc, clientAddress); } /* * Lookup SessionContext from SessionId. Do not create new SessionContext if * SessionId is not found. * * @param sessionId */ public static SessionContext lookupSessionContextBySessionId( String sessionId) throws StorageFault { return SessionContext.lookupSessionBySessionId(sessionId, false, null, null); } /* * If any exist, free the resources for the SessionContext associated with * the given sessionId. * * @param sessionId */ public static void removeSession(String sessionId) throws StorageFault { removeFromList(SessionContext.lookupSessionBySessionId(sessionId, false, null, null)); } @SuppressWarnings("squid:S00100") // ("Suppressing Sonar violation for Method names should comply with a naming convention") public static boolean IsPreviouslyUsed(String sessionId) { return sessionIdList.contains(sessionId); } }