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
.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
= ContentUtils
.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 ProvidedContent
get(ProvidedSession contentSession
, String relativePath
) {
66 String jcrPath
= "/" + relativePath
;
67 return new JcrContent(contentSession
, this, jcrWorkspace
, jcrPath
);
71 public boolean exists(ProvidedSession contentSession
, String relativePath
) {
72 String jcrWorkspace
= ContentUtils
.getParentPath(mountPath
)[1];
73 String jcrPath
= "/" + relativePath
;
74 return new JcrContent(contentSession
, this, jcrWorkspace
, jcrPath
).exists();
77 public Session
getJcrSession(ProvidedSession contentSession
, String jcrWorkspace
) {
78 JcrSessionAdapter sessionAdapter
= sessionAdapters
.get(contentSession
);
79 if (sessionAdapter
== null) {
80 final JcrSessionAdapter newSessionAdapter
= new JcrSessionAdapter(jcrRepository
, contentSession
,
81 contentSession
.getSubject());
82 sessionAdapters
.put(contentSession
, newSessionAdapter
);
83 contentSession
.onClose().thenAccept((s
) -> newSessionAdapter
.close());
84 sessionAdapter
= newSessionAdapter
;
87 Session jcrSession
= sessionAdapter
.getSession(jcrWorkspace
);
91 public Session
getJcrSession(Content content
, String jcrWorkspace
) {
92 return getJcrSession(((ProvidedContent
) content
).getSession(), jcrWorkspace
);
96 public String
getMountPath() {
100 public synchronized <T
> T
doInAdminSession(Function
<Session
, T
> toDo
) {
102 return toDo
.apply(adminSession
);
105 if (adminSession
.hasPendingChanges())
107 } catch (RepositoryException e
) {
108 throw new JcrException("Cannot save admin session", e
);
117 public synchronized String
getNamespaceURI(String prefix
) {
119 return adminSession
.getNamespaceURI(prefix
);
120 } catch (RepositoryException e
) {
121 throw new JcrException(e
);
126 public synchronized String
getPrefix(String namespaceURI
) {
128 return adminSession
.getNamespacePrefix(namespaceURI
);
129 } catch (RepositoryException e
) {
130 throw new JcrException(e
);
135 public synchronized Iterator
<String
> getPrefixes(String namespaceURI
) {
137 return Arrays
.asList(adminSession
.getNamespacePrefix(namespaceURI
)).iterator();
138 } catch (RepositoryException e
) {
139 throw new JcrException(e
);
148 public Spliterator
<Content
> search(ProvidedSession session
, BasicSearch search
, String relPath
) {
150 Session jcrSession
= getJcrSession(session
, jcrWorkspace
);
151 BasicSearchToQom jcrBasicSearch
= new BasicSearchToQom(jcrSession
, search
, relPath
);
152 Query query
= jcrBasicSearch
.createQuery();
153 QueryResult queryResult
= query
.execute();
154 return new QueryResultSpliterator(session
, queryResult
.getNodes());
155 } catch (RepositoryException e
) {
156 throw new JcrException(e
);
160 class QueryResultSpliterator
implements Spliterator
<Content
> {
161 private ProvidedSession providedSession
;
162 private NodeIterator nodeIterator
;
164 public QueryResultSpliterator(ProvidedSession providedSession
, NodeIterator nodeIterator
) {
166 this.providedSession
= providedSession
;
167 this.nodeIterator
= nodeIterator
;
171 public boolean tryAdvance(Consumer
<?
super Content
> action
) {
172 if (!nodeIterator
.hasNext())
175 Node node
= nodeIterator
.nextNode();
176 // TODO optimise by reusing the Node
177 JcrContent jcrContent
= new JcrContent(providedSession
, JcrContentProvider
.this, jcrWorkspace
,
179 action
.accept(jcrContent
);
181 } catch (RepositoryException e
) {
182 throw new JcrException(e
);
187 public Spliterator
<Content
> trySplit() {
192 public long estimateSize() {
193 return nodeIterator
.getSize();
197 public int characteristics() {
198 return NONNULL
| SIZED
;