2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org
.argeo
.jcr
;
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
;
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
;
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
;
38 /** Proxy JCR sessions and attach them to calling threads. */
39 public class ThreadBoundJcrSessionFactory
implements FactoryBean
,
41 private final static Log log
= LogFactory
42 .getLog(ThreadBoundJcrSessionFactory
.class);
44 private Repository repository
;
45 private final List
<Session
> activeSessions
= Collections
46 .synchronizedList(new ArrayList
<Session
>());
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;
54 private String defaultUsername
= "demo";
55 private String defaultPassword
= "demo";
56 private Boolean forceDefaultCredentials
= false;
58 public ThreadBoundJcrSessionFactory() {
59 Class
<?
>[] interfaces
= { Session
.class };
60 proxiedSession
= (Session
) Proxy
.newProxyInstance(getClass()
61 .getClassLoader(), interfaces
,
62 new JcrSessionInvocationHandler());
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
71 if (!forceDefaultCredentials
)
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
);
82 // log using default username / password (useful for testing purposes)
83 if (newSession
== null)
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
);
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
);
100 public Object
getObject() {
101 return proxiedSession
;
104 public void destroy() throws Exception
{
105 if (log
.isDebugEnabled())
106 log
.debug("Cleaning up " + activeSessions
.size()
107 + " active JCR sessions...");
110 for (Session sess
: activeSessions
) {
113 activeSessions
.clear();
116 public Class
<?
extends Session
> getObjectType() {
117 return Session
.class;
120 public boolean isSingleton() {
124 public void setRepository(Repository repository
) {
125 this.repository
= repository
;
128 public void setDefaultUsername(String defaultUsername
) {
129 this.defaultUsername
= defaultUsername
;
132 public void setDefaultPassword(String defaultPassword
) {
133 this.defaultPassword
= defaultPassword
;
136 public void setForceDefaultCredentials(Boolean forceDefaultCredentials
) {
137 this.forceDefaultCredentials
= forceDefaultCredentials
;
140 public void setWorkspace(String workspace
) {
141 this.workspace
= workspace
;
144 protected class JcrSessionInvocationHandler
implements InvocationHandler
{
146 public Object
invoke(Object proxy
, Method method
, Object
[] args
)
148 Session threadSession
= session
.get();
149 if (threadSession
== null) {
150 if ("logout".equals(method
.getName()))// no need to login
152 else if ("toString".equals(method
.getName()))// maybe logging
153 return "Uninitialized Argeo thread bound JCR session";
154 threadSession
= login();
155 session
.set(threadSession
);
158 Object ret
= method
.invoke(threadSession
, args
);
159 if ("logout".equals(method
.getName())) {
162 activeSessions
.remove(threadSession
);
163 if (log
.isTraceEnabled())
164 log
.trace("Logged out from JCR session " + threadSession
165 + "; userId=" + threadSession
.getUserID());