/* * * This module intended to be used for session replication of Jetty via HBase * and later will be cached via Ehcache * * Copyright (C) 2010 Imran M Yousuf (imyousuf@smartitengineering.com) * * This library 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 3 of the License, or (at your option) any later version. * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package com.smartitengineering.jetty.session.replication; import com.smartitengineering.jetty.session.replication.SmartSessionManager.Session; import java.util.Date; import java.util.Random; import java.util.concurrent.locks.ReentrantLock; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; import org.apache.commons.lang.StringUtils; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.SessionManager; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.session.AbstractSessionIdManager; import org.eclipse.jetty.server.session.SessionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author imyousuf */ public class SmartSessionIdManager extends AbstractSessionIdManager { protected final Logger logger = LoggerFactory.getLogger(getClass()); private final Server server; private final ReentrantLock lock = new ReentrantLock(); public SmartSessionIdManager(Server server, Random random) { super(random); this.server = server; } public SmartSessionIdManager(Server server) { this.server = server; } @Override protected void doStart() throws Exception { super.doStart(); } @Override public boolean idInUse(String id) { if (logger.isInfoEnabled()) { logger.info("idInUse " + id); } if (StringUtils.isBlank(id)) { return false; } String clusterId = getClusterId(id); boolean inUse = false; Cache sessionIds = SessionReplicationAPI.getInstance().getSessionIdCache(); if (logger.isInfoEnabled()) { logger.info("Checking for " + clusterId + " in " + sessionIds); } inUse = sessionIds.isKeyInCache(clusterId); if (inUse) { return true; //optimisation - if this session is one we've been managing, we can check locally } //otherwise, we need to go to the database to check try { return SessionReplicationAPI.getInstance().getIdReader().getById(clusterId) != null; } catch (Exception e) { logger.warn("Problem checking inUse for id=" + clusterId, e); return false; } } @Override public void addSession(HttpSession httpSession) { logger.info("addSession"); if (httpSession == null || !(httpSession instanceof Session)) { return; } lock.lock(); try { Session session = (Session) httpSession; SessionId sessionId = new SessionId(); sessionId.setCreatedAt(new Date().getTime()); final String id = session.getClusterId(); sessionId.setId(id); if (logger.isInfoEnabled()) { logger.info("Session id " + sessionId.getId() + " " + sessionId.getCreatedAt()); } SessionReplicationAPI.getInstance().getIdWriter().save(sessionId); Cache sessionIds = SessionReplicationAPI.getInstance().getSessionIdCache(); sessionIds.put(new Element(id, System.currentTimeMillis())); } catch (Exception ex) { logger.error("Could not add session id!", ex); } finally { lock.unlock(); } } @Override public void removeSession(HttpSession httpSession) { logger.info("removeSession"); lock.lock(); try { if (httpSession == null || !(httpSession instanceof Session)) { return; } Session session = (Session) httpSession; final String clusterId = session.getClusterId(); removeSession(clusterId); } finally { lock.unlock(); } } protected void removeSession(final String clusterId) { logger.info("removeSession"); lock.lock(); try { SessionId sessionId = SessionReplicationAPI.getInstance().getIdReader().getById(clusterId); if (sessionId != null) { SessionReplicationAPI.getInstance().getIdWriter().delete(sessionId); Cache sessionIds = SessionReplicationAPI.getInstance().getSessionIdCache(); sessionIds.remove(sessionId.getId()); } } catch (Exception ex) { logger.error("Could not remove session id!", ex); } finally { lock.unlock(); } } @Override public void invalidateAll(String id) { logger.info("invalidateAll"); //take the id out of the list of known sessionids for this node lock.lock(); try { removeSession(id); //tell all contexts that may have a session object with this id to //get rid of them Handler[] contexts = server.getChildHandlersByClass(ContextHandler.class); for (int i = 0; contexts != null && i < contexts.length; i++) { SessionHandler sessionHandler = (SessionHandler) ((ContextHandler) contexts[i]).getChildHandlerByClass( SessionHandler.class); if (sessionHandler != null) { SessionManager manager = sessionHandler.getSessionManager(); if (manager != null && manager instanceof SmartSessionManager) { ((SmartSessionManager) manager).invalidateSession(id); } } } } finally { lock.unlock(); } } @Override public String getClusterId(String nodeId) { logger.info("getClusterId"); int dot = nodeId.lastIndexOf('.'); return (dot > 0) ? nodeId.substring(0, dot) : nodeId; } @Override public String getNodeId(String clusterId, HttpServletRequest request) { logger.info("getNodeId"); if (getWorkerName() != null) { return clusterId + '.' + getWorkerName(); } return clusterId; } }