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
.ContentUtils
;
29 import org
.argeo
.cms
.jcr
.CmsJcrUtils
;
30 import org
.argeo
.cms
.jcr
.acr
.search
.JcrBasicSearch
;
31 import org
.argeo
.jcr
.JcrException
;
32 import org
.argeo
.jcr
.JcrUtils
;
34 /** A JCR workspace accessed as an {@link ContentProvider}. */
35 public class JcrContentProvider
implements ContentProvider
, NamespaceContext
{
37 private Repository jcrRepository
;
38 private Session adminSession
;
40 private String mountPath
;
43 private String jcrWorkspace
;
45 private Map
<ProvidedSession
, JcrSessionAdapter
> sessionAdapters
= Collections
.synchronizedMap(new HashMap
<>());
47 public void start(Map
<String
, String
> properties
) {
48 mountPath
= properties
.get(CmsConstants
.ACR_MOUNT_PATH
);
49 if ("/".equals(mountPath
))
50 throw new IllegalArgumentException("JCR content provider cannot be root /");
51 Objects
.requireNonNull(mountPath
);
52 jcrWorkspace
= ContentUtils
.getParentPath(mountPath
)[1];
53 adminSession
= CmsJcrUtils
.openDataAdminSession(jcrRepository
, jcrWorkspace
);
57 if (adminSession
.isLive())
58 JcrUtils
.logoutQuietly(adminSession
);
61 public void setJcrRepository(Repository jcrRepository
) {
62 this.jcrRepository
= jcrRepository
;
66 public ProvidedContent
get(ProvidedSession contentSession
, String relativePath
) {
67 String jcrPath
= "/" + relativePath
;
68 return new JcrContent(contentSession
, this, jcrWorkspace
, jcrPath
);
72 public boolean exists(ProvidedSession contentSession
, String relativePath
) {
73 String jcrWorkspace
= ContentUtils
.getParentPath(mountPath
)[1];
74 String jcrPath
= "/" + relativePath
;
75 return new JcrContent(contentSession
, this, jcrWorkspace
, jcrPath
).exists();
78 public Session
getJcrSession(ProvidedSession contentSession
, String jcrWorkspace
) {
79 JcrSessionAdapter sessionAdapter
= sessionAdapters
.get(contentSession
);
80 if (sessionAdapter
== null) {
81 final JcrSessionAdapter newSessionAdapter
= new JcrSessionAdapter(jcrRepository
, contentSession
,
82 contentSession
.getSubject());
83 sessionAdapters
.put(contentSession
, newSessionAdapter
);
84 contentSession
.onClose().thenAccept((s
) -> newSessionAdapter
.close());
85 sessionAdapter
= newSessionAdapter
;
88 Session jcrSession
= sessionAdapter
.getSession(jcrWorkspace
);
92 public Session
getJcrSession(Content content
, String jcrWorkspace
) {
93 return getJcrSession(((ProvidedContent
) content
).getSession(), jcrWorkspace
);
97 public String
getMountPath() {
101 public synchronized <T
> T
doInAdminSession(Function
<Session
, T
> toDo
) {
103 return toDo
.apply(adminSession
);
106 if (adminSession
.hasPendingChanges())
108 } catch (RepositoryException e
) {
109 throw new JcrException("Cannot save admin session", e
);
118 public synchronized String
getNamespaceURI(String prefix
) {
120 return adminSession
.getNamespaceURI(prefix
);
121 } catch (RepositoryException e
) {
122 throw new JcrException(e
);
127 public synchronized String
getPrefix(String namespaceURI
) {
129 return adminSession
.getNamespacePrefix(namespaceURI
);
130 } catch (RepositoryException e
) {
131 throw new JcrException(e
);
136 public synchronized Iterator
<String
> getPrefixes(String namespaceURI
) {
138 return Arrays
.asList(adminSession
.getNamespacePrefix(namespaceURI
)).iterator();
139 } catch (RepositoryException e
) {
140 throw new JcrException(e
);
149 public Spliterator
<Content
> search(ProvidedSession session
, BasicSearch search
, String relPath
) {
151 Session jcrSession
= getJcrSession(session
, jcrWorkspace
);
152 JcrBasicSearch jcrBasicSearch
= new JcrBasicSearch(jcrSession
, search
, relPath
);
153 Query query
= jcrBasicSearch
.createQuery();
154 QueryResult queryResult
= query
.execute();
155 return new QueryResultSpliterator(session
, queryResult
.getNodes());
156 } catch (RepositoryException e
) {
157 throw new JcrException(e
);
161 class QueryResultSpliterator
implements Spliterator
<Content
> {
162 private ProvidedSession providedSession
;
163 private NodeIterator nodeIterator
;
165 public QueryResultSpliterator(ProvidedSession providedSession
, NodeIterator nodeIterator
) {
167 this.providedSession
= providedSession
;
168 this.nodeIterator
= nodeIterator
;
172 public boolean tryAdvance(Consumer
<?
super Content
> action
) {
173 if (!nodeIterator
.hasNext())
176 Node node
= nodeIterator
.nextNode();
177 // TODO optimise by reusing the Node
178 JcrContent jcrContent
= new JcrContent(providedSession
, JcrContentProvider
.this, jcrWorkspace
,
180 action
.accept(jcrContent
);
182 } catch (RepositoryException e
) {
183 throw new JcrException(e
);
188 public Spliterator
<Content
> trySplit() {
193 public long estimateSize() {
194 return nodeIterator
.getSize();
198 public int characteristics() {
199 return NONNULL
| SIZED
;