]> git.argeo.org Git - gpl/argeo-jcr.git/blob - org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java
Merge tag 'v2.3.14' into testing
[gpl/argeo-jcr.git] / org.argeo.cms.jcr / src / org / argeo / cms / jcr / acr / JcrContentProvider.java
1 package org.argeo.cms.jcr.acr;
2
3 import java.util.Arrays;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.Iterator;
7 import java.util.Map;
8 import java.util.Objects;
9 import java.util.Spliterator;
10 import java.util.function.Consumer;
11 import java.util.function.Function;
12
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;
21
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;
32
33 /** A JCR workspace accessed as an {@link ContentProvider}. */
34 public class JcrContentProvider implements ContentProvider, NamespaceContext {
35
36 private Repository jcrRepository;
37 private Session adminSession;
38
39 private String mountPath;
40
41 // cache
42 private String jcrWorkspace;
43
44 private Map<ProvidedSession, JcrSessionAdapter> sessionAdapters = Collections.synchronizedMap(new HashMap<>());
45
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);
53 }
54
55 public void stop() {
56 if (adminSession.isLive())
57 JcrUtils.logoutQuietly(adminSession);
58 }
59
60 public void setJcrRepository(Repository jcrRepository) {
61 this.jcrRepository = jcrRepository;
62 }
63
64 @Override
65 public ProvidedContent get(ProvidedSession contentSession, String relativePath) {
66 String jcrPath = "/" + relativePath;
67 return new JcrContent(contentSession, this, jcrWorkspace, jcrPath);
68 }
69
70 @Override
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();
75 }
76
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;
85 }
86
87 Session jcrSession = sessionAdapter.getSession(jcrWorkspace);
88 return jcrSession;
89 }
90
91 public Session getJcrSession(Content content, String jcrWorkspace) {
92 return getJcrSession(((ProvidedContent) content).getSession(), jcrWorkspace);
93 }
94
95 @Override
96 public String getMountPath() {
97 return mountPath;
98 }
99
100 public synchronized <T> T doInAdminSession(Function<Session, T> toDo) {
101 try {
102 return toDo.apply(adminSession);
103 } finally {
104 try {
105 if (adminSession.hasPendingChanges())
106 adminSession.save();
107 } catch (RepositoryException e) {
108 throw new JcrException("Cannot save admin session", e);
109 }
110 }
111 }
112
113 /*
114 * NAMESPACE CONTEXT
115 */
116 @Override
117 public synchronized String getNamespaceURI(String prefix) {
118 try {
119 return adminSession.getNamespaceURI(prefix);
120 } catch (RepositoryException e) {
121 throw new JcrException(e);
122 }
123 }
124
125 @Override
126 public synchronized String getPrefix(String namespaceURI) {
127 try {
128 return adminSession.getNamespacePrefix(namespaceURI);
129 } catch (RepositoryException e) {
130 throw new JcrException(e);
131 }
132 }
133
134 @Override
135 public synchronized Iterator<String> getPrefixes(String namespaceURI) {
136 try {
137 return Arrays.asList(adminSession.getNamespacePrefix(namespaceURI)).iterator();
138 } catch (RepositoryException e) {
139 throw new JcrException(e);
140 }
141 }
142
143 /*
144 * SEARCH
145 */
146
147 @Override
148 public Spliterator<Content> search(ProvidedSession session, BasicSearch search, String relPath) {
149 try {
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);
157 }
158 }
159
160 class QueryResultSpliterator implements Spliterator<Content> {
161 private ProvidedSession providedSession;
162 private NodeIterator nodeIterator;
163
164 public QueryResultSpliterator(ProvidedSession providedSession, NodeIterator nodeIterator) {
165 super();
166 this.providedSession = providedSession;
167 this.nodeIterator = nodeIterator;
168 }
169
170 @Override
171 public boolean tryAdvance(Consumer<? super Content> action) {
172 if (!nodeIterator.hasNext())
173 return false;
174 try {
175 Node node = nodeIterator.nextNode();
176 // TODO optimise by reusing the Node
177 JcrContent jcrContent = new JcrContent(providedSession, JcrContentProvider.this, jcrWorkspace,
178 node.getPath());
179 action.accept(jcrContent);
180 return true;
181 } catch (RepositoryException e) {
182 throw new JcrException(e);
183 }
184 }
185
186 @Override
187 public Spliterator<Content> trySplit() {
188 return null;
189 }
190
191 @Override
192 public long estimateSize() {
193 return nodeIterator.getSize();
194 }
195
196 @Override
197 public int characteristics() {
198 return NONNULL | SIZED;
199 }
200
201 }
202 }