]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/http/CmsSessionProvider.java
Prepare JCR decoupling.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / http / CmsSessionProvider.java
1 package org.argeo.cms.internal.http;
2
3 import java.io.Serializable;
4 import java.security.PrivilegedActionException;
5 import java.security.PrivilegedExceptionAction;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.LinkedHashMap;
9 import java.util.Map;
10 import java.util.Set;
11
12 import javax.jcr.Repository;
13 import javax.jcr.RepositoryException;
14 import javax.jcr.Session;
15 import javax.security.auth.Subject;
16 import javax.servlet.ServletException;
17 import javax.servlet.http.HttpServletRequest;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.apache.jackrabbit.server.SessionProvider;
22 import org.argeo.api.NodeConstants;
23 import org.argeo.cms.auth.CmsSession;
24 import org.argeo.cms.internal.auth.CmsSessionImpl;
25 import org.argeo.jcr.JcrUtils;
26
27 /**
28 * Implements an open session in view patter: a new JCR session is created for
29 * each request
30 */
31 public class CmsSessionProvider implements SessionProvider, Serializable {
32 private static final long serialVersionUID = -1358136599534938466L;
33
34 private final static Log log = LogFactory.getLog(CmsSessionProvider.class);
35
36 private final String alias;
37
38 private LinkedHashMap<Session, CmsDataSession> cmsSessions = new LinkedHashMap<>();
39
40 public CmsSessionProvider(String alias) {
41 this.alias = alias;
42 }
43
44 public Session getSession(HttpServletRequest request, Repository rep, String workspace)
45 throws javax.jcr.LoginException, ServletException, RepositoryException {
46
47 // a client is scanning parent URLs.
48 // if (workspace == null)
49 // return null;
50
51 CmsSessionImpl cmsSession = WebCmsSessionImpl.getCmsSession(request);
52 if (log.isTraceEnabled()) {
53 log.trace("Get JCR session from " + cmsSession);
54 }
55 if (cmsSession == null)
56 throw new IllegalStateException("Cannot find a session for request " + request.getRequestURI());
57 CmsDataSession cmsDataSession = new CmsDataSession(cmsSession);
58 Session session = cmsDataSession.getDataSession(alias, workspace, rep);
59 cmsSessions.put(session, cmsDataSession);
60 return session;
61 }
62
63 public void releaseSession(Session session) {
64 // JcrUtils.logoutQuietly(session);
65 if (cmsSessions.containsKey(session)) {
66 CmsDataSession cmsDataSession = cmsSessions.get(session);
67 cmsDataSession.releaseDataSession(alias, session);
68 } else {
69 log.warn("JCR session " + session + " not found in CMS session list. Logging it out...");
70 JcrUtils.logoutQuietly(session);
71 }
72 }
73
74 static class CmsDataSession {
75 private CmsSession cmsSession;
76
77 private Map<String, Session> dataSessions = new HashMap<>();
78 private Set<String> dataSessionsInUse = new HashSet<>();
79 private Set<Session> additionalDataSessions = new HashSet<>();
80
81 private CmsDataSession(CmsSession cmsSession) {
82 this.cmsSession = cmsSession;
83 }
84
85 public Session newDataSession(String cn, String workspace, Repository repository) {
86 checkValid();
87 return login(repository, workspace);
88 }
89
90 public synchronized Session getDataSession(String cn, String workspace, Repository repository) {
91 checkValid();
92 // FIXME make it more robust
93 if (workspace == null)
94 workspace = NodeConstants.SYS_WORKSPACE;
95 String path = cn + '/' + workspace;
96 if (dataSessionsInUse.contains(path)) {
97 try {
98 wait(1000);
99 if (dataSessionsInUse.contains(path)) {
100 Session session = login(repository, workspace);
101 additionalDataSessions.add(session);
102 if (log.isTraceEnabled())
103 log.trace("Additional data session " + path + " for " + cmsSession.getUserDn());
104 return session;
105 }
106 } catch (InterruptedException e) {
107 // silent
108 }
109 }
110
111 Session session = null;
112 if (dataSessions.containsKey(path)) {
113 session = dataSessions.get(path);
114 } else {
115 session = login(repository, workspace);
116 dataSessions.put(path, session);
117 if (log.isTraceEnabled())
118 log.trace("New data session " + path + " for " + cmsSession.getUserDn());
119 }
120 dataSessionsInUse.add(path);
121 return session;
122 }
123
124 private Session login(Repository repository, String workspace) {
125 try {
126 return Subject.doAs(cmsSession.getSubject(), new PrivilegedExceptionAction<Session>() {
127 @Override
128 public Session run() throws Exception {
129 return repository.login(workspace);
130 }
131 });
132 } catch (PrivilegedActionException e) {
133 throw new IllegalStateException("Cannot log in " + cmsSession.getUserDn() + " to JCR", e);
134 }
135 }
136
137 public synchronized void releaseDataSession(String cn, Session session) {
138 if (additionalDataSessions.contains(session)) {
139 JcrUtils.logoutQuietly(session);
140 additionalDataSessions.remove(session);
141 if (log.isTraceEnabled())
142 log.trace("Remove additional data session " + session);
143 return;
144 }
145 String path = cn + '/' + session.getWorkspace().getName();
146 if (!dataSessionsInUse.contains(path))
147 log.warn("Data session " + path + " was not in use for " + cmsSession.getUserDn());
148 dataSessionsInUse.remove(path);
149 Session registeredSession = dataSessions.get(path);
150 if (session != registeredSession)
151 log.warn("Data session " + path + " not consistent for " + cmsSession.getUserDn());
152 if (log.isTraceEnabled())
153 log.trace("Released data session " + session + " for " + path);
154 notifyAll();
155 }
156
157 private void checkValid() {
158 if (!cmsSession.isValid())
159 throw new IllegalStateException(
160 "CMS session " + cmsSession.getUuid() + " is not valid since " + cmsSession.getEnd());
161 }
162
163 private void close() {
164 // FIXME class this when CMS session is closed
165 synchronized (this) {
166 // TODO check data session in use ?
167 for (String path : dataSessions.keySet())
168 JcrUtils.logoutQuietly(dataSessions.get(path));
169 for (Session session : additionalDataSessions)
170 JcrUtils.logoutQuietly(session);
171 }
172 }
173 }
174 }