]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.server.jcr/src/org/argeo/jcr/ThreadBoundJcrSessionFactory.java
Remove DocBook from defaults CNDs
[lgpl/argeo-commons.git] / org.argeo.server.jcr / src / org / argeo / jcr / ThreadBoundJcrSessionFactory.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
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 package org.argeo.jcr;
17
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.InvocationTargetException;
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.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28
29 import javax.jcr.LoginException;
30 import javax.jcr.Repository;
31 import javax.jcr.RepositoryException;
32 import javax.jcr.Session;
33 import javax.jcr.SimpleCredentials;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.argeo.ArgeoException;
38
39 /** Proxy JCR sessions and attach them to calling threads. */
40 @Deprecated
41 public abstract class ThreadBoundJcrSessionFactory {
42 private final static Log log = LogFactory
43 .getLog(ThreadBoundJcrSessionFactory.class);
44
45 private Repository repository;
46 /** can be injected as list, only used if repository is null */
47 private List<Repository> repositories;
48
49 private ThreadLocal<Session> session = new ThreadLocal<Session>();
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 private boolean active = true;
59
60 // monitoring
61 private final List<Thread> threads = Collections
62 .synchronizedList(new ArrayList<Thread>());
63 private final Map<Long, Session> activeSessions = Collections
64 .synchronizedMap(new HashMap<Long, Session>());
65 private MonitoringThread monitoringThread;
66
67 public ThreadBoundJcrSessionFactory() {
68 Class<?>[] interfaces = { Session.class };
69 proxiedSession = (Session) Proxy.newProxyInstance(
70 ThreadBoundJcrSessionFactory.class.getClassLoader(),
71 interfaces, new JcrSessionInvocationHandler());
72 }
73
74 /** Logs in to the repository using various strategies. */
75 protected synchronized Session login() {
76 if (!isActive())
77 throw new ArgeoException("Thread bound session factory inactive");
78
79 // discard session previously attached to this thread
80 Thread thread = Thread.currentThread();
81 if (activeSessions.containsKey(thread.getId())) {
82 Session oldSession = activeSessions.remove(thread.getId());
83 oldSession.logout();
84 session.remove();
85 }
86
87 Session newSession = null;
88 // first try to login without credentials, assuming the underlying login
89 // module will have dealt with authentication (typically using Spring
90 // Security)
91 if (!forceDefaultCredentials)
92 try {
93 newSession = repository().login(workspace);
94 } catch (LoginException e1) {
95 log.warn("Cannot login without credentials: " + e1.getMessage());
96 // invalid credentials, go to the next step
97 } catch (RepositoryException e1) {
98 // other kind of exception, fail
99 throw new ArgeoException("Cannot log in to repository", e1);
100 }
101
102 // log using default username / password (useful for testing purposes)
103 if (newSession == null)
104 try {
105 SimpleCredentials sc = new SimpleCredentials(defaultUsername,
106 defaultPassword.toCharArray());
107 newSession = repository().login(sc, workspace);
108 } catch (RepositoryException e) {
109 throw new ArgeoException("Cannot log in to repository", e);
110 }
111
112 session.set(newSession);
113 // Log and monitor new session
114 if (log.isTraceEnabled())
115 log.trace("Logged in to JCR session " + newSession + "; userId="
116 + newSession.getUserID());
117
118 // monitoring
119 activeSessions.put(thread.getId(), newSession);
120 threads.add(thread);
121 return newSession;
122 }
123
124 public Object getObject() {
125 return proxiedSession;
126 }
127
128 public void init() throws Exception {
129 monitoringThread = new MonitoringThread();
130 monitoringThread.start();
131 }
132
133 public void dispose() throws Exception {
134 // if (activeSessions.size() == 0)
135 // return;
136
137 if (log.isTraceEnabled())
138 log.trace("Cleaning up " + activeSessions.size()
139 + " active JCR sessions...");
140
141 deactivate();
142 for (Session sess : activeSessions.values()) {
143 JcrUtils.logoutQuietly(sess);
144 }
145 activeSessions.clear();
146 }
147
148 protected Boolean isActive() {
149 return active;
150 }
151
152 protected synchronized void deactivate() {
153 active = false;
154 notifyAll();
155 }
156
157 protected synchronized void removeSession(Thread thread) {
158 if (!isActive())
159 return;
160 activeSessions.remove(thread.getId());
161 threads.remove(thread);
162 }
163
164 protected synchronized void cleanDeadThreads() {
165 if (!isActive())
166 return;
167 Iterator<Thread> it = threads.iterator();
168 while (it.hasNext()) {
169 Thread thread = it.next();
170 if (!thread.isAlive() && isActive()) {
171 if (activeSessions.containsKey(thread.getId())) {
172 Session session = activeSessions.get(thread.getId());
173 activeSessions.remove(thread.getId());
174 session.logout();
175 if (log.isTraceEnabled())
176 log.trace("Cleaned up JCR session (userID="
177 + session.getUserID() + ") from dead thread "
178 + thread.getId());
179 }
180 it.remove();
181 }
182 }
183 try {
184 wait(1000);
185 } catch (InterruptedException e) {
186 // silent
187 }
188 }
189
190 public Class<? extends Session> getObjectType() {
191 return Session.class;
192 }
193
194 public boolean isSingleton() {
195 return true;
196 }
197
198 /**
199 * Called before a method is actually called, allowing to check the session
200 * or re-login it (e.g. if authentication has changed). The default
201 * implementation returns the session.
202 */
203 protected Session preCall(Session session) {
204 return session;
205 }
206
207 protected Repository repository() {
208 if (repository != null)
209 return repository;
210 if (repositories != null) {
211 // hardened for OSGi dynamic services
212 Iterator<Repository> it = repositories.iterator();
213 if (it.hasNext())
214 return it.next();
215 }
216 throw new ArgeoException("No repository injected");
217 }
218
219 // /** Useful for declarative registration of OSGi services (blueprint) */
220 // public void register(Repository repository, Map<?, ?> params) {
221 // this.repository = repository;
222 // }
223 //
224 // /** Useful for declarative registration of OSGi services (blueprint) */
225 // public void unregister(Repository repository, Map<?, ?> params) {
226 // this.repository = null;
227 // }
228
229 public void setRepository(Repository repository) {
230 this.repository = repository;
231 }
232
233 public void setRepositories(List<Repository> repositories) {
234 this.repositories = repositories;
235 }
236
237 public void setDefaultUsername(String defaultUsername) {
238 this.defaultUsername = defaultUsername;
239 }
240
241 public void setDefaultPassword(String defaultPassword) {
242 this.defaultPassword = defaultPassword;
243 }
244
245 public void setForceDefaultCredentials(Boolean forceDefaultCredentials) {
246 this.forceDefaultCredentials = forceDefaultCredentials;
247 }
248
249 public void setWorkspace(String workspace) {
250 this.workspace = workspace;
251 }
252
253 protected class JcrSessionInvocationHandler implements InvocationHandler {
254
255 public Object invoke(Object proxy, Method method, Object[] args)
256 throws Throwable, RepositoryException {
257 Session threadSession = session.get();
258 if (threadSession == null) {
259 if ("logout".equals(method.getName()))// no need to login
260 return Void.TYPE;
261 else if ("toString".equals(method.getName()))// maybe logging
262 return "Uninitialized Argeo thread bound JCR session";
263 threadSession = login();
264 }
265
266 preCall(threadSession);
267 Object ret;
268 try {
269 ret = method.invoke(threadSession, args);
270 } catch (InvocationTargetException e) {
271 Throwable cause = e.getCause();
272 if (cause instanceof RepositoryException)
273 throw (RepositoryException) cause;
274 else
275 throw cause;
276 }
277 if ("logout".equals(method.getName())) {
278 session.remove();
279 Thread thread = Thread.currentThread();
280 removeSession(thread);
281 if (log.isTraceEnabled())
282 log.trace("Logged out JCR session (userId="
283 + threadSession.getUserID() + ") on thread "
284 + thread.getId());
285 }
286 return ret;
287 }
288 }
289
290 /** Monitors registered thread in order to clean up dead ones. */
291 private class MonitoringThread extends Thread {
292
293 public MonitoringThread() {
294 super("ThreadBound JCR Session Monitor");
295 }
296
297 @Override
298 public void run() {
299 while (isActive()) {
300 cleanDeadThreads();
301 }
302 }
303
304 }
305 }