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