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 jcrPath
= ContentUtils
.SLASH
+ relativePath
;
73 return new JcrContent(contentSession
, this, jcrWorkspace
, jcrPath
).exists();
76 public Session
getJcrSession(ProvidedSession contentSession
, String jcrWorkspace
) {
77 JcrSessionAdapter sessionAdapter
= sessionAdapters
.get(contentSession
);
78 if (sessionAdapter
== null) {
79 final JcrSessionAdapter newSessionAdapter
= new JcrSessionAdapter(jcrRepository
, contentSession
,
80 contentSession
.getSubject());
81 sessionAdapters
.put(contentSession
, newSessionAdapter
);
82 contentSession
.onClose().thenAccept((s
) -> newSessionAdapter
.close());
83 sessionAdapter
= newSessionAdapter
;
86 Session jcrSession
= sessionAdapter
.getSession(jcrWorkspace
);
90 public Session
getJcrSession(Content content
, String jcrWorkspace
) {
91 return getJcrSession(((ProvidedContent
) content
).getSession(), jcrWorkspace
);
95 public String
getMountPath() {
99 public synchronized <T
> T
doInAdminSession(Function
<Session
, T
> toDo
) {
101 return toDo
.apply(adminSession
);
104 if (adminSession
.hasPendingChanges())
106 } catch (RepositoryException e
) {
107 throw new JcrException("Cannot save admin session", e
);
116 public synchronized String
getNamespaceURI(String prefix
) {
118 return adminSession
.getNamespaceURI(prefix
);
119 } catch (RepositoryException e
) {
120 throw new JcrException(e
);
125 public synchronized String
getPrefix(String namespaceURI
) {
127 return adminSession
.getNamespacePrefix(namespaceURI
);
128 } catch (RepositoryException e
) {
129 throw new JcrException(e
);
134 public synchronized Iterator
<String
> getPrefixes(String namespaceURI
) {
136 return Arrays
.asList(adminSession
.getNamespacePrefix(namespaceURI
)).iterator();
137 } catch (RepositoryException e
) {
138 throw new JcrException(e
);
147 public Spliterator
<Content
> search(ProvidedSession session
, BasicSearch search
, String relPath
) {
149 Session jcrSession
= getJcrSession(session
, jcrWorkspace
);
150 BasicSearchToQom jcrBasicSearch
= new BasicSearchToQom(jcrSession
, search
, relPath
);
151 Query query
= jcrBasicSearch
.createQuery();
152 QueryResult queryResult
= query
.execute();
153 return new QueryResultSpliterator(session
, queryResult
.getNodes());
154 } catch (RepositoryException e
) {
155 throw new JcrException(e
);
159 class QueryResultSpliterator
implements Spliterator
<Content
> {
160 private ProvidedSession providedSession
;
161 private NodeIterator nodeIterator
;
163 public QueryResultSpliterator(ProvidedSession providedSession
, NodeIterator nodeIterator
) {
165 this.providedSession
= providedSession
;
166 this.nodeIterator
= nodeIterator
;
170 public boolean tryAdvance(Consumer
<?
super Content
> action
) {
171 if (!nodeIterator
.hasNext())
174 Node node
= nodeIterator
.nextNode();
175 // TODO optimise by reusing the Node
176 JcrContent jcrContent
= new JcrContent(providedSession
, JcrContentProvider
.this, jcrWorkspace
,
178 action
.accept(jcrContent
);
180 } catch (RepositoryException e
) {
181 throw new JcrException(e
);
186 public Spliterator
<Content
> trySplit() {
191 public long estimateSize() {
192 return nodeIterator
.getSize();
196 public int characteristics() {
197 return NONNULL
| SIZED
;