/*
* Copyright (C) 2009 eXo Platform SAS.
*
* 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.exoplatform.services.jcr.impl.core;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.management.annotations.Managed;
import org.exoplatform.management.annotations.ManagedDescription;
import org.exoplatform.management.jmx.annotations.NameTemplate;
import org.exoplatform.management.jmx.annotations.Property;
import org.exoplatform.services.jcr.config.RepositoryEntry;
import org.exoplatform.services.jcr.impl.proccess.WorkerThread;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.picocontainer.Startable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author <a href="mailto:Sergey.Kabashnyuk@gmail.com">Sergey Kabashnyuk</a>
* @version $Id: SessionRegistry.java 12049 2008-03-18 12:22:03Z gazarenkov $
*/
@Managed
@NameTemplate(@Property(key = "service", value = "SessionRegistry"))
public final class SessionRegistry implements Startable
{
private final Map<String, SessionImpl> sessionsMap;
// 1 min
public final static int DEFAULT_CLEANER_TIMEOUT = 60 * 1000;
// 10 mins
public final static int DEFAULT_SESSION_TIMEOUT = 10 * 60 * 1000;
protected static Log log = ExoLogger.getLogger("exo.jcr.component.core.SessionRegistry");
private volatile SessionCleaner sessionCleaner;
private String repositoryId;
protected volatile long timeOut;
@Managed
@ManagedDescription("How many sessions are currently active")
public int getSize()
{
return sessionsMap.size();
}
@Managed
@ManagedDescription("The session time out")
public long getTimeOut()
{
return timeOut;
}
@Managed
@ManagedDescription("Set the session time out in seconds")
public void setTimeOut(long timeout)
{
if (this.timeOut == timeout)
{
return;
}
// disable the cleaner
this.sessionCleaner.halt();
this.sessionCleaner = null;
this.timeOut = timeout <= 0 ? 0 : timeout * 1000;
if (timeOut == 0 && sessionCleaner != null)
{
// We set a time out to 0, no need to create new cleaner
if (log.isDebugEnabled())
{
log.debug("Stop the previous session cleaner");
}
}
else if (timeOut > 0 && sessionCleaner == null)
{
// We set a time out greater than 0, so we create new cleaner
this.sessionCleaner = new SessionCleaner(repositoryId, DEFAULT_CLEANER_TIMEOUT, timeOut);
if (log.isDebugEnabled())
{
log.debug("Start a new session cleaner");
}
}
}
@Managed
@ManagedDescription("Perform a cleanup of timed out sessions")
public void runCleanup()
{
if (sessionCleaner != null)
{
try
{
sessionCleaner.callPeriodically();
}
catch (Exception e)
{
log.warn("Could not execute the cleanup command", e);
}
}
}
public SessionRegistry(RepositoryEntry entry)
{
this(null, entry);
}
public SessionRegistry(ExoContainerContext ctx, RepositoryEntry entry)
{
sessionsMap = new ConcurrentHashMap<String, SessionImpl>();
if (entry != null)
{
this.timeOut = entry.getSessionTimeOut() > 0 ? entry.getSessionTimeOut() : DEFAULT_SESSION_TIMEOUT;
}
this.repositoryId = ctx != null ? ctx.getName() : (entry == null ? null : entry.getName());
}
public void registerSession(SessionImpl session)
{
sessionsMap.put(session.getId(), session);
}
public void unregisterSession(String sessionId)
{
sessionsMap.remove(sessionId);
}
public SessionImpl getSession(String sessionId)
{
return sessionId == null ? null : sessionsMap.get(sessionId);
}
public boolean isInUse(String workspaceName)
{
if (workspaceName == null)
{
if (log.isDebugEnabled())
{
log.debug("Session in use " + sessionsMap.size());
}
return sessionsMap.size() > 0;
}
for (SessionImpl session : sessionsMap.values())
{
if (session.getWorkspace().getName().equals(workspaceName))
{
if (log.isDebugEnabled())
{
log.debug("Session for workspace " + workspaceName + " in use." + " Session id:" + session.getId()
+ " user: " + session.getUserID());
}
return true;
}
}
return false;
}
public void start()
{
sessionsMap.clear();
if (timeOut > 0)
{
sessionCleaner = new SessionCleaner(repositoryId, DEFAULT_CLEANER_TIMEOUT, timeOut);
}
}
public void stop()
{
if (timeOut > 0 && sessionCleaner != null)
{
sessionCleaner.halt();
}
// Close all the current sessions to prevent memory leaks
for (SessionImpl s : sessionsMap.values())
{
try
{
s.logout();
}
catch (Exception e)
{
if (log.isDebugEnabled())
{
log.debug("Could not close the session", e);
}
}
}
sessionsMap.clear();
}
/**
* closeSessions will be closed the all sessions on specific workspace.
*
* @param workspaceName
* the workspace name.
* @return int
* how many sessions was closed.
*/
public int closeSessions(String workspaceName)
{
int closedSessions = 0;
for (SessionImpl session : sessionsMap.values())
{
if (session.getWorkspace().getName().equals(workspaceName))
{
session.logout();
closedSessions++;
}
}
return closedSessions;
}
private class SessionCleaner extends WorkerThread
{
private final long sessionTimeOut;
public SessionCleaner(String id, long workTime, long sessionTimeOut)
{
super(workTime);
this.sessionTimeOut = sessionTimeOut;
setName("Session Cleaner " + (id == null ? getId() : id));
setPriority(Thread.MIN_PRIORITY);
setDaemon(true);
start();
if (log.isDebugEnabled())
{
log.debug("SessionCleaner instantiated name= " + getName() + " workTime= " + workTime + " sessionTimeOut="
+ sessionTimeOut);
}
}
@Override
protected void callPeriodically() throws Exception
{
for (SessionImpl session : sessionsMap.values())
{
if (session.getLastAccessTime() + getTimeout(session) < System.currentTimeMillis())
{
session.expire();
}
}
}
/**
* Checks if the session has a local timeout if so it will use it otherwise it will use the
* global timeout
*/
private long getTimeout(SessionImpl session)
{
long localSessionTimeout = session.getTimeout();
return localSessionTimeout == 0 ? sessionTimeOut : localSessionTimeout;
}
}
}