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