]> git.argeo.org Git - gpl/argeo-jcr.git/blob - org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/servlet/CmsSessionProvider.java
Improve writing to a JCR Content
[gpl/argeo-jcr.git] / org.argeo.cms.jcr / src / org / argeo / cms / jcr / internal / servlet / CmsSessionProvider.java
1 package org.argeo.cms.jcr.internal.servlet;
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.jackrabbit.server.SessionProvider;
20 import org.argeo.api.cms.CmsConstants;
21 import org.argeo.api.cms.CmsLog;
22 import org.argeo.api.cms.CmsSession;
23 import org.argeo.cms.auth.RemoteAuthUtils;
24 import org.argeo.cms.servlet.ServletHttpRequest;
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 CmsLog log = CmsLog.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 CmsSession cmsSession = RemoteAuthUtils.getCmsSession(new ServletHttpRequest(request));
52 // CmsSessionImpl cmsSession = WebCmsSessionImpl.getCmsSession(request);
53 if (log.isTraceEnabled()) {
54 log.trace("Get JCR session from " + cmsSession);
55 }
56 if (cmsSession == null)
57 throw new IllegalStateException("Cannot find a session for request " + request.getRequestURI());
58 CmsDataSession cmsDataSession = new CmsDataSession(cmsSession);
59 Session session = cmsDataSession.getDataSession(alias, workspace, rep);
60 cmsSessions.put(session, cmsDataSession);
61 return session;
62 }
63
64 public void releaseSession(Session session) {
65 // JcrUtils.logoutQuietly(session);
66 if (cmsSessions.containsKey(session)) {
67 CmsDataSession cmsDataSession = cmsSessions.get(session);
68 cmsDataSession.releaseDataSession(alias, session);
69 } else {
70 log.warn("JCR session " + session + " not found in CMS session list. Logging it out...");
71 JcrUtils.logoutQuietly(session);
72 }
73 }
74
75 static class CmsDataSession {
76 private CmsSession cmsSession;
77
78 private Map<String, Session> dataSessions = new HashMap<>();
79 private Set<String> dataSessionsInUse = new HashSet<>();
80 private Set<Session> additionalDataSessions = new HashSet<>();
81
82 private CmsDataSession(CmsSession cmsSession) {
83 this.cmsSession = cmsSession;
84 cmsSession.addOnCloseCallback((sess) -> close());
85 }
86
87 public Session newDataSession(String cn, String workspace, Repository repository) {
88 checkValid();
89 return login(repository, workspace);
90 }
91
92 public synchronized Session getDataSession(String cn, String workspace, Repository repository) {
93 checkValid();
94 // FIXME make it more robust
95 if (workspace == null)
96 workspace = CmsConstants.SYS_WORKSPACE;
97 String path = cn + '/' + workspace;
98 if (dataSessionsInUse.contains(path)) {
99 try {
100 wait(1000);
101 if (dataSessionsInUse.contains(path)) {
102 Session session = login(repository, workspace);
103 additionalDataSessions.add(session);
104 if (log.isTraceEnabled())
105 log.trace("Additional data session " + path + " for " + cmsSession.getUserDn());
106 return session;
107 }
108 } catch (InterruptedException e) {
109 // silent
110 }
111 }
112
113 Session session = null;
114 if (dataSessions.containsKey(path)) {
115 session = dataSessions.get(path);
116 } else {
117 session = login(repository, workspace);
118 dataSessions.put(path, session);
119 if (log.isTraceEnabled())
120 log.trace("New data session " + path + " for " + cmsSession.getUserDn());
121 }
122 dataSessionsInUse.add(path);
123 return session;
124 }
125
126 private Session login(Repository repository, String workspace) {
127 try {
128 return Subject.doAs(cmsSession.getSubject(), new PrivilegedExceptionAction<Session>() {
129 @Override
130 public Session run() throws Exception {
131 return repository.login(workspace);
132 }
133 });
134 } catch (PrivilegedActionException e) {
135 throw new IllegalStateException("Cannot log in " + cmsSession.getUserDn() + " to JCR", e);
136 }
137 }
138
139 public synchronized void releaseDataSession(String cn, Session session) {
140 if (additionalDataSessions.contains(session)) {
141 JcrUtils.logoutQuietly(session);
142 additionalDataSessions.remove(session);
143 if (log.isTraceEnabled())
144 log.trace("Remove additional data session " + session);
145 return;
146 }
147 String path = cn + '/' + session.getWorkspace().getName();
148 if (!dataSessionsInUse.contains(path))
149 log.warn("Data session " + path + " was not in use for " + cmsSession.getUserDn());
150 dataSessionsInUse.remove(path);
151 Session registeredSession = dataSessions.get(path);
152 if (session != registeredSession)
153 log.warn("Data session " + path + " not consistent for " + cmsSession.getUserDn());
154 if (log.isTraceEnabled())
155 log.trace("Released data session " + session + " for " + path);
156 notifyAll();
157 }
158
159 private void checkValid() {
160 if (!cmsSession.isValid())
161 throw new IllegalStateException(
162 "CMS session " + cmsSession.uuid() + " is not valid since " + cmsSession.getEnd());
163 }
164
165 protected void close() {
166 synchronized (this) {
167 // TODO check data session in use ?
168 for (String path : dataSessions.keySet())
169 JcrUtils.logoutQuietly(dataSessions.get(path));
170 for (Session session : additionalDataSessions)
171 JcrUtils.logoutQuietly(session);
172 }
173 }
174 }
175 }