package org.argeo.jcr; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; 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; public class ThreadBoundJcrSessionFactory implements FactoryBean, DisposableBean { private final static Log log = LogFactory .getLog(ThreadBoundJcrSessionFactory.class); private Repository repository; private List activeSessions = Collections .synchronizedList(new ArrayList()); private ThreadLocal session = new ThreadLocal(); private boolean destroying = false; private final Session proxiedSession; public ThreadBoundJcrSessionFactory() { Class[] interfaces = { Session.class }; proxiedSession = (Session) Proxy.newProxyInstance(getClass() .getClassLoader(), interfaces, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Session threadSession = session.get(); if (threadSession == null) { if ("logout".equals(method.getName()))// no need to login return Void.TYPE; threadSession = login(); session.set(threadSession); } Object ret = method.invoke(threadSession, args); if ("logout".equals(method.getName())) { session.remove(); if (!destroying) activeSessions.remove(threadSession); if (log.isTraceEnabled()) log.trace("Logged out from JCR session " + threadSession + "; userId=" + threadSession.getUserID()); } return ret; } }); } protected Session login() { try { Session sess = repository.login(); if (log.isTraceEnabled()) log.trace("Log in to JCR session " + sess + "; userId=" + sess.getUserID()); // Thread.dumpStack(); activeSessions.add(sess); return sess; } catch (RepositoryException e) { throw new ArgeoException("Cannot log in to repository", e); } } public Object getObject() { return proxiedSession; } public void destroy() throws Exception { if (log.isDebugEnabled()) log.debug("Cleaning up " + activeSessions.size() + " active JCR sessions..."); destroying = true; for (Session sess : activeSessions) { sess.logout(); } activeSessions.clear(); } public Class getObjectType() { return Session.class; } public boolean isSingleton() { return true; } public void setRepository(Repository repository) { this.repository = repository; } }