X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=server%2Fruntime%2Forg.argeo.server.jcr%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fjcr%2FThreadBoundJcrSessionFactory.java;h=d548b6eddaa227a63be3449994e08ba6ad6da326;hb=149023e5969377045847bbecf24b0898b18a67a9;hp=9428c69593a5237f4f6237979f69e9b8e650bd60;hpb=2f510fb09e18bc3d3e902c8131d0037763c5f279;p=lgpl%2Fargeo-commons.git diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ThreadBoundJcrSessionFactory.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ThreadBoundJcrSessionFactory.java index 9428c6959..d548b6edd 100644 --- a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ThreadBoundJcrSessionFactory.java +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ThreadBoundJcrSessionFactory.java @@ -21,7 +21,10 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; import javax.jcr.LoginException; import javax.jcr.Repository; @@ -32,21 +35,15 @@ import javax.jcr.SimpleCredentials; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.ArgeoException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.FactoryBean; /** Proxy JCR sessions and attach them to calling threads. */ -public class ThreadBoundJcrSessionFactory implements FactoryBean, - DisposableBean { +public class ThreadBoundJcrSessionFactory { private final static Log log = LogFactory .getLog(ThreadBoundJcrSessionFactory.class); private Repository repository; - private final List activeSessions = Collections - .synchronizedList(new ArrayList()); private ThreadLocal session = new ThreadLocal(); - private boolean destroying = false; private final Session proxiedSession; /** If workspace is null, default will be used. */ private String workspace = null; @@ -55,6 +52,15 @@ public class ThreadBoundJcrSessionFactory implements FactoryBean, private String defaultPassword = "demo"; private Boolean forceDefaultCredentials = false; + private boolean active = true; + + // monitoring + private final List threads = Collections + .synchronizedList(new ArrayList()); + private final Map activeSessions = Collections + .synchronizedMap(new HashMap()); + private MonitoringThread monitoringThread; + public ThreadBoundJcrSessionFactory() { Class[] interfaces = { Session.class }; proxiedSession = (Session) Proxy.newProxyInstance(getClass() @@ -64,6 +70,17 @@ public class ThreadBoundJcrSessionFactory implements FactoryBean, /** Logs in to the repository using various strategies. */ protected Session login() { + if (!isActive()) + throw new ArgeoException("Thread bound session factory inactive"); + + // discard session previously attached to this thread + Thread thread = Thread.currentThread(); + if (activeSessions.containsKey(thread.getId())) { + Session oldSession = activeSessions.remove(thread.getId()); + oldSession.logout(); + session.remove(); + } + Session newSession = null; // first try to login without credentials, assuming the underlying login // module will have dealt with authentication (typically using Spring @@ -89,11 +106,15 @@ public class ThreadBoundJcrSessionFactory implements FactoryBean, throw new ArgeoException("Cannot log in to repository", e); } + session.set(newSession); // Log and monitor new session if (log.isTraceEnabled()) log.trace("Logged in to JCR session " + newSession + "; userId=" + newSession.getUserID()); - activeSessions.add(newSession); + + // monitoring + activeSessions.put(thread.getId(), newSession); + threads.add(thread); return newSession; } @@ -101,18 +122,68 @@ public class ThreadBoundJcrSessionFactory implements FactoryBean, return proxiedSession; } - public void destroy() throws Exception { + public void init() throws Exception { + monitoringThread = new MonitoringThread(); + monitoringThread.start(); + } + + public synchronized void dispose() throws Exception { + if (activeSessions.size() == 0) + return; + if (log.isDebugEnabled()) log.debug("Cleaning up " + activeSessions.size() + " active JCR sessions..."); - destroying = true; - for (Session sess : activeSessions) { + deactivate(); + for (Session sess : activeSessions.values()) { sess.logout(); } activeSessions.clear(); } + protected Boolean isActive() { + return active; + } + + protected synchronized void deactivate() { + active = false; + notifyAll(); + } + + protected synchronized void removeSession(Thread thread) { + if (!isActive()) + return; + activeSessions.remove(thread.getId()); + threads.remove(thread); + } + + protected synchronized void cleanDeadThreads() { + if (!isActive()) + return; + Iterator it = threads.iterator(); + while (it.hasNext()) { + Thread thread = it.next(); + if (!thread.isAlive() && isActive()) { + if (activeSessions.containsKey(thread.getId())) { + Session session = activeSessions.get(thread.getId()); + activeSessions.remove(thread.getId()); + session.logout(); + if (log.isDebugEnabled()) + log.debug("Cleaned up JCR session (userID=" + + session.getUserID() + ") from dead thread " + + thread.getId()); + } + it.remove(); + } + } + try { + wait(1000); + } catch (InterruptedException e) { + // silent + } + } + public Class getObjectType() { return Session.class; } @@ -121,6 +192,15 @@ public class ThreadBoundJcrSessionFactory implements FactoryBean, return true; } + /** + * Called before a method is actually called, allowing to check the session + * or re-login it (e.g. if authentication has changed). The default + * implementation returns the session. + */ + protected Session preCall(Session session) { + return session; + } + public void setRepository(Repository repository) { this.repository = repository; } @@ -152,28 +232,32 @@ public class ThreadBoundJcrSessionFactory implements FactoryBean, else if ("toString".equals(method.getName()))// maybe logging return "Uninitialized Argeo thread bound JCR session"; threadSession = login(); - session.set(threadSession); } + preCall(threadSession); Object ret = method.invoke(threadSession, args); if ("logout".equals(method.getName())) { session.remove(); - if (!destroying) - activeSessions.remove(threadSession); + Thread thread = Thread.currentThread(); + removeSession(thread); if (log.isTraceEnabled()) - log.trace("Logged out from JCR session " + threadSession - + "; userId=" + threadSession.getUserID()); + log.trace("Logged out JCR session (userId=" + + threadSession.getUserID() + ") on thread " + + thread.getId()); } return ret; } } - - protected class MonitoringThread extends Thread{ + + /** Monitors registered thread in order to clean up dead ones. */ + private class MonitoringThread extends Thread { @Override public void run() { - Thread thread=null; + while (isActive()) { + cleanDeadThreads(); + } } - + } }