1 package org
.argeo
.cms
.jcr
.acr
;
3 import java
.util
.Arrays
;
4 import java
.util
.Collections
;
5 import java
.util
.HashMap
;
6 import java
.util
.Iterator
;
8 import java
.util
.Objects
;
9 import java
.util
.Spliterator
;
10 import java
.util
.function
.Consumer
;
11 import java
.util
.function
.Function
;
13 import javax
.jcr
.Node
;
14 import javax
.jcr
.NodeIterator
;
15 import javax
.jcr
.Repository
;
16 import javax
.jcr
.RepositoryException
;
17 import javax
.jcr
.Session
;
18 import javax
.jcr
.query
.Query
;
19 import javax
.jcr
.query
.QueryResult
;
20 import javax
.xml
.namespace
.NamespaceContext
;
22 import org
.argeo
.api
.acr
.Content
;
23 import org
.argeo
.api
.acr
.search
.BasicSearch
;
24 import org
.argeo
.api
.acr
.spi
.ContentProvider
;
25 import org
.argeo
.api
.acr
.spi
.ProvidedContent
;
26 import org
.argeo
.api
.acr
.spi
.ProvidedSession
;
27 import org
.argeo
.api
.cms
.CmsConstants
;
28 import org
.argeo
.cms
.acr
.CmsContent
;
29 import org
.argeo
.cms
.jcr
.CmsJcrUtils
;
30 import org
.argeo
.jcr
.JcrException
;
31 import org
.argeo
.jcr
.JcrUtils
;
33 /** A JCR workspace accessed as an {@link ContentProvider}. */
34 public class JcrContentProvider
implements ContentProvider
, NamespaceContext
{
36 private Repository jcrRepository
;
37 private Session adminSession
;
39 private String mountPath
;
42 private String jcrWorkspace
;
44 private Map
<ProvidedSession
, JcrSessionAdapter
> sessionAdapters
= Collections
.synchronizedMap(new HashMap
<>());
46 public void start(Map
<String
, String
> properties
) {
47 mountPath
= properties
.get(CmsConstants
.ACR_MOUNT_PATH
);
48 if ("/".equals(mountPath
))
49 throw new IllegalArgumentException("JCR content provider cannot be root /");
50 Objects
.requireNonNull(mountPath
);
51 jcrWorkspace
= CmsContent
.getParentPath(mountPath
)[1];
52 adminSession
= CmsJcrUtils
.openDataAdminSession(jcrRepository
, jcrWorkspace
);
56 if (adminSession
.isLive())
57 JcrUtils
.logoutQuietly(adminSession
);
60 public void setJcrRepository(Repository jcrRepository
) {
61 this.jcrRepository
= jcrRepository
;
65 public String
getMountPath() {
74 public ProvidedContent
get(ProvidedSession contentSession
, String relativePath
) {
75 return new JcrContent(contentSession
, this, jcrWorkspace
, toJcrPath(relativePath
));
79 public boolean exists(ProvidedSession contentSession
, String relativePath
) {
80 String jcrPath
= '/' + relativePath
;
81 return new JcrContent(contentSession
, this, jcrWorkspace
, jcrPath
).exists();
84 protected JcrSessionAdapter
getJcrSessionAdapter(ProvidedSession contentSession
) {
85 JcrSessionAdapter sessionAdapter
= sessionAdapters
.get(contentSession
);
86 if (sessionAdapter
== null) {
87 final JcrSessionAdapter newSessionAdapter
= new JcrSessionAdapter(jcrRepository
, contentSession
,
88 contentSession
.getSubject());
89 sessionAdapters
.put(contentSession
, newSessionAdapter
);
90 contentSession
.onClose().thenAccept((s
) -> newSessionAdapter
.close());
91 sessionAdapter
= newSessionAdapter
;
93 return sessionAdapter
;
96 public Session
getJcrSession(ProvidedSession contentSession
, String jcrWorkspace
) {
97 JcrSessionAdapter sessionAdapter
= getJcrSessionAdapter(contentSession
);
98 Session jcrSession
= sessionAdapter
.getSession(jcrWorkspace
);
102 public Session
getJcrSession(Content content
, String jcrWorkspace
) {
103 return getJcrSession(((ProvidedContent
) content
).getSession(), jcrWorkspace
);
109 Node
openForEdit(ProvidedSession contentSession
, String jcrWorkspace
, String jcrPath
) {
111 if (contentSession
.isEditing()) {
112 JcrSessionAdapter sessionAdapter
= getJcrSessionAdapter(contentSession
);
113 return sessionAdapter
.openForEdit(jcrWorkspace
, jcrPath
);
115 return getJcrSession(contentSession
, jcrWorkspace
).getNode(jcrPath
);
117 } catch (RepositoryException e
) {
118 throw new JcrException("Cannot open for edit " + jcrPath
+ " in workspace " + jcrWorkspace
, e
);
123 public void persist(ProvidedSession contentSession
) {
125 JcrSessionAdapter sessionAdapter
= getJcrSessionAdapter(contentSession
);
126 sessionAdapter
.persist();
127 } catch (RepositoryException e
) {
128 throw new JcrException("Cannot persist " + contentSession
, e
);
137 public void openForEdit(ProvidedSession session
, String relativePath
) {
138 openForEdit(session
, relativePath
, toJcrPath(relativePath
));
142 public void freeze(ProvidedSession session
, String relativePath
) {
144 String jcrPath
= toJcrPath(relativePath
);
145 if (session
.isEditing()) {
146 JcrSessionAdapter sessionAdapter
= getJcrSessionAdapter(session
);
147 sessionAdapter
.freeze(jcrWorkspace
, jcrPath
);
149 } catch (RepositoryException e
) {
150 throw new JcrException("Cannot freeze " + relativePath
+ " in workspace " + jcrWorkspace
, e
);
155 public boolean isOpenForEdit(ProvidedSession session
, String relativePath
) {
157 String jcrPath
= toJcrPath(relativePath
);
158 JcrSessionAdapter sessionAdapter
= getJcrSessionAdapter(session
);
159 return sessionAdapter
.isOpenForEdit(jcrWorkspace
, jcrPath
);
160 } catch (RepositoryException e
) {
161 throw new JcrException(
162 "Cannot check whether " + relativePath
+ " is open for edit in workspace " + jcrWorkspace
, e
);
170 public synchronized String
getNamespaceURI(String prefix
) {
172 return adminSession
.getNamespaceURI(prefix
);
173 } catch (RepositoryException e
) {
174 throw new JcrException(e
);
179 public synchronized String
getPrefix(String namespaceURI
) {
181 return adminSession
.getNamespacePrefix(namespaceURI
);
182 } catch (RepositoryException e
) {
183 throw new JcrException(e
);
188 public synchronized Iterator
<String
> getPrefixes(String namespaceURI
) {
190 return Arrays
.asList(adminSession
.getNamespacePrefix(namespaceURI
)).iterator();
191 } catch (RepositoryException e
) {
192 throw new JcrException(e
);
201 public Spliterator
<Content
> search(ProvidedSession session
, BasicSearch search
, String relPath
) {
203 Session jcrSession
= getJcrSession(session
, jcrWorkspace
);
204 BasicSearchToQom jcrBasicSearch
= new BasicSearchToQom(jcrSession
, search
, relPath
);
205 Query query
= jcrBasicSearch
.createQuery();
206 QueryResult queryResult
= query
.execute();
207 return new QueryResultSpliterator(session
, queryResult
.getNodes());
208 } catch (RepositoryException e
) {
209 throw new JcrException(e
);
213 class QueryResultSpliterator
implements Spliterator
<Content
> {
214 private ProvidedSession providedSession
;
215 private NodeIterator nodeIterator
;
217 public QueryResultSpliterator(ProvidedSession providedSession
, NodeIterator nodeIterator
) {
219 this.providedSession
= providedSession
;
220 this.nodeIterator
= nodeIterator
;
224 public boolean tryAdvance(Consumer
<?
super Content
> action
) {
225 if (!nodeIterator
.hasNext())
228 Node node
= nodeIterator
.nextNode();
229 // TODO optimise by reusing the Node
230 JcrContent jcrContent
= new JcrContent(providedSession
, JcrContentProvider
.this, jcrWorkspace
,
232 action
.accept(jcrContent
);
234 } catch (RepositoryException e
) {
235 throw new JcrException(e
);
240 public Spliterator
<Content
> trySplit() {
245 public long estimateSize() {
246 return nodeIterator
.getSize();
250 public int characteristics() {
251 return NONNULL
| SIZED
;
260 * Just adds a '/' so that it becomes an absolute JCR path within the JCR
261 * workspace of this provider.
263 private String
toJcrPath(String relativePath
) {
264 return '/' + relativePath
;
268 * TRANSITIONAL, WHILE MIGRATING FROM JCR TO ACR
271 public synchronized <T
> T
doInAdminSession(Function
<Session
, T
> toDo
) {
273 return toDo
.apply(adminSession
);
276 if (adminSession
.hasPendingChanges())
278 } catch (RepositoryException e
) {
279 throw new JcrException("Cannot save admin session", e
);