]> git.argeo.org Git - lgpl/argeo-commons.git/blob - ThreadBoundJcrSessionFactory.java
aaafdc26a704f2781c11164c07aec708a3a00769
[lgpl/argeo-commons.git] / ThreadBoundJcrSessionFactory.java
1 /*
2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.argeo.jcr;
18
19 import java.lang.reflect.InvocationHandler;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Proxy;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25
26 import javax.jcr.LoginException;
27 import javax.jcr.Repository;
28 import javax.jcr.RepositoryException;
29 import javax.jcr.Session;
30 import javax.jcr.SimpleCredentials;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.argeo.ArgeoException;
35 import org.springframework.beans.factory.DisposableBean;
36 import org.springframework.beans.factory.FactoryBean;
37
38 /** Proxy JCR sessions and attach them to calling threads. */
39 public class ThreadBoundJcrSessionFactory implements FactoryBean,
40 DisposableBean {
41 private final static Log log = LogFactory
42 .getLog(ThreadBoundJcrSessionFactory.class);
43
44 private Repository repository;
45 private final List<Session> activeSessions = Collections
46 .synchronizedList(new ArrayList<Session>());
47
48 private ThreadLocal<Session> session = new ThreadLocal<Session>();
49 private boolean destroying = false;
50 private final Session proxiedSession;
51 /** If workspace is null, default will be used. */
52 private String workspace = null;
53
54 private String defaultUsername = "demo";
55 private String defaultPassword = "demo";
56 private Boolean forceDefaultCredentials = false;
57
58 public ThreadBoundJcrSessionFactory() {
59 Class<?>[] interfaces = { Session.class };
60 proxiedSession = (Session) Proxy.newProxyInstance(getClass()
61 .getClassLoader(), interfaces,
62 new JcrSessionInvocationHandler());
63 }
64
65 /** Logs in to the repository using various strategies. */
66 protected Session login() {
67 Session newSession = null;
68 // first try to login without credentials, assuming the underlying login
69 // module will have dealt with authentication (typically using Spring
70 // Security)
71 if (!forceDefaultCredentials)
72 try {
73 newSession = repository.login(workspace);
74 } catch (LoginException e1) {
75 log.warn("Cannot login without credentials: " + e1.getMessage());
76 // invalid credentials, go to the next step
77 } catch (RepositoryException e1) {
78 // other kind of exception, fail
79 throw new ArgeoException("Cannot log in to repository", e1);
80 }
81
82 // log using default username / password (useful for testing purposes)
83 if (newSession == null)
84 try {
85 SimpleCredentials sc = new SimpleCredentials(defaultUsername,
86 defaultPassword.toCharArray());
87 newSession = repository.login(sc, workspace);
88 } catch (RepositoryException e) {
89 throw new ArgeoException("Cannot log in to repository", e);
90 }
91
92 // Log and monitor new session
93 if (log.isTraceEnabled())
94 log.trace("Logged in to JCR session " + newSession + "; userId="
95 + newSession.getUserID());
96 activeSessions.add(newSession);
97 return newSession;
98 }
99
100 public Object getObject() {
101 return proxiedSession;
102 }
103
104 public void destroy() throws Exception {
105 if (log.isDebugEnabled())
106 log.debug("Cleaning up " + activeSessions.size()
107 + " active JCR sessions...");
108
109 destroying = true;
110 for (Session sess : activeSessions) {
111 sess.logout();
112 }
113 activeSessions.clear();
114 }
115
116 public Class<? extends Session> getObjectType() {
117 return Session.class;
118 }
119
120 public boolean isSingleton() {
121 return true;
122 }
123
124 public void setRepository(Repository repository) {
125 this.repository = repository;
126 }
127
128 public void setDefaultUsername(String defaultUsername) {
129 this.defaultUsername = defaultUsername;
130 }
131
132 public void setDefaultPassword(String defaultPassword) {
133 this.defaultPassword = defaultPassword;
134 }
135
136 public void setForceDefaultCredentials(Boolean forceDefaultCredentials) {
137 this.forceDefaultCredentials = forceDefaultCredentials;
138 }
139
140 public void setWorkspace(String workspace) {
141 this.workspace = workspace;
142 }
143
144 protected class JcrSessionInvocationHandler implements InvocationHandler {
145
146 public Object invoke(Object proxy, Method method, Object[] args)
147 throws Throwable {
148 Session threadSession = session.get();
149 if (threadSession == null) {
150 if ("logout".equals(method.getName()))// no need to login
151 return Void.TYPE;
152 else if ("toString".equals(method.getName()))// maybe logging
153 return "Uninitialized Argeo thread bound JCR session";
154 threadSession = login();
155 session.set(threadSession);
156 }
157
158 Object ret = method.invoke(threadSession, args);
159 if ("logout".equals(method.getName())) {
160 session.remove();
161 if (!destroying)
162 activeSessions.remove(threadSession);
163 if (log.isTraceEnabled())
164 log.trace("Logged out from JCR session " + threadSession
165 + "; userId=" + threadSession.getUserID());
166 }
167 return ret;
168 }
169 }
170 }