]> git.argeo.org Git - lgpl/argeo-commons.git/blob - jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java
Admin session to the proper content provider workspace
[lgpl/argeo-commons.git] / jcr / org.argeo.cms.jcr / src / org / argeo / cms / jcr / acr / JcrContent.java
1 package org.argeo.cms.jcr.acr;
2
3 import java.io.Closeable;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.PipedInputStream;
7 import java.io.PipedOutputStream;
8 import java.util.ArrayList;
9 import java.util.Calendar;
10 import java.util.HashSet;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Optional;
14 import java.util.Set;
15 import java.util.TreeSet;
16 import java.util.concurrent.ForkJoinPool;
17
18 import javax.jcr.Node;
19 import javax.jcr.NodeIterator;
20 import javax.jcr.Property;
21 import javax.jcr.PropertyIterator;
22 import javax.jcr.PropertyType;
23 import javax.jcr.RepositoryException;
24 import javax.jcr.Value;
25 import javax.jcr.nodetype.NodeType;
26 import javax.xml.namespace.QName;
27 import javax.xml.transform.Source;
28 import javax.xml.transform.stream.StreamSource;
29
30 import org.argeo.api.acr.Content;
31 import org.argeo.api.acr.NamespaceUtils;
32 import org.argeo.api.acr.spi.ContentProvider;
33 import org.argeo.api.acr.spi.ProvidedSession;
34 import org.argeo.api.cms.CmsConstants;
35 import org.argeo.cms.acr.AbstractContent;
36 import org.argeo.cms.acr.ContentUtils;
37 import org.argeo.jcr.Jcr;
38 import org.argeo.jcr.JcrException;
39 import org.argeo.jcr.JcrUtils;
40
41 /** A JCR {@link Node} accessed as {@link Content}. */
42 public class JcrContent extends AbstractContent {
43 // private Node jcrNode;
44
45 private JcrContentProvider provider;
46
47 private String jcrWorkspace;
48 private String jcrPath;
49
50 protected JcrContent(ProvidedSession session, JcrContentProvider provider, String jcrWorkspace, String jcrPath) {
51 super(session);
52 this.provider = provider;
53 this.jcrWorkspace = jcrWorkspace;
54 this.jcrPath = jcrPath;
55 }
56
57 @Override
58 public QName getName() {
59 String name = Jcr.getName(getJcrNode());
60 if (name.equals("")) {// root
61 String mountPath = provider.getMountPath();
62 name = ContentUtils.getParentPath(mountPath)[1];
63 // name = Jcr.getWorkspaceName(getJcrNode());
64 }
65 return NamespaceUtils.parsePrefixedName(provider, name);
66 }
67
68 @SuppressWarnings("unchecked")
69 @Override
70 public <A> Optional<A> get(QName key, Class<A> clss) {
71 if (isDefaultAttrTypeRequested(clss)) {
72 return Optional.of((A) get(getJcrNode(), key.toString()));
73 }
74 return Optional.of((A) Jcr.get(getJcrNode(), key.toString()));
75 }
76
77 @Override
78 public Iterator<Content> iterator() {
79 try {
80 return new JcrContentIterator(getJcrNode().getNodes());
81 } catch (RepositoryException e) {
82 throw new JcrException("Cannot list children of " + getJcrNode(), e);
83 }
84 }
85
86 @Override
87 protected Iterable<QName> keys() {
88 try {
89 Set<QName> keys = new HashSet<>();
90 for (PropertyIterator propertyIterator = getJcrNode().getProperties(); propertyIterator.hasNext();) {
91 Property property = propertyIterator.nextProperty();
92 // TODO convert standard names
93 // TODO skip technical properties
94 QName name = NamespaceUtils.parsePrefixedName(provider, property.getName());
95 keys.add(name);
96 }
97 return keys;
98 } catch (RepositoryException e) {
99 throw new JcrException("Cannot list properties of " + getJcrNode(), e);
100 }
101 }
102
103 public Node getJcrNode() {
104 try {
105 // TODO caching?
106 return provider.getJcrSession(getSession(), jcrWorkspace).getNode(jcrPath);
107 } catch (RepositoryException e) {
108 throw new JcrException("Cannot retrieve " + jcrPath + " from workspace " + jcrWorkspace, e);
109 }
110 }
111
112 /** Cast to a standard Java object. */
113 static Object get(Node node, String property) {
114 try {
115 Property p = node.getProperty(property);
116 if (p.isMultiple()) {
117 Value[] values = p.getValues();
118 List<Object> lst = new ArrayList<>();
119 for (Value value : values) {
120 lst.add(convertSingleValue(value));
121 }
122 return lst;
123 } else {
124 Value value = node.getProperty(property).getValue();
125 return convertSingleValue(value);
126 }
127 } catch (RepositoryException e) {
128 throw new JcrException("Cannot cast value from " + property + " of node " + node, e);
129 }
130 }
131
132 static Object convertSingleValue(Value value) throws RepositoryException {
133 switch (value.getType()) {
134 case PropertyType.STRING:
135 return value.getString();
136 case PropertyType.DOUBLE:
137 return (Double) value.getDouble();
138 case PropertyType.LONG:
139 return (Long) value.getLong();
140 case PropertyType.BOOLEAN:
141 return (Boolean) value.getBoolean();
142 case PropertyType.DATE:
143 Calendar calendar = value.getDate();
144 return calendar.toInstant();
145 case PropertyType.BINARY:
146 throw new IllegalArgumentException("Binary is not supported as an attribute");
147 default:
148 return value.getString();
149 }
150 }
151
152 class JcrContentIterator implements Iterator<Content> {
153 private final NodeIterator nodeIterator;
154 // we keep track in order to be able to delete it
155 private JcrContent current = null;
156
157 protected JcrContentIterator(NodeIterator nodeIterator) {
158 this.nodeIterator = nodeIterator;
159 }
160
161 @Override
162 public boolean hasNext() {
163 return nodeIterator.hasNext();
164 }
165
166 @Override
167 public Content next() {
168 current = new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getPath(nodeIterator.nextNode()));
169 return current;
170 }
171
172 @Override
173 public void remove() {
174 if (current != null) {
175 Jcr.remove(current.getJcrNode());
176 }
177 }
178
179 }
180
181 @Override
182 public Content getParent() {
183 if (Jcr.isRoot(getJcrNode())) // root
184 return null;
185 return new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getParentPath(getJcrNode()));
186 }
187
188 @Override
189 public Content add(QName name, QName... classes) {
190 if (classes.length > 0) {
191 QName primaryType = classes[0];
192 Node child = Jcr.addNode(getJcrNode(), name.toString(), primaryType.toString());
193 for (int i = 1; i < classes.length; i++) {
194 try {
195 child.addMixin(classes[i].toString());
196 } catch (RepositoryException e) {
197 throw new JcrException("Cannot add child to " + getJcrNode(), e);
198 }
199 }
200
201 } else {
202 Jcr.addNode(getJcrNode(), name.toString(), NodeType.NT_UNSTRUCTURED);
203 }
204 return null;
205 }
206
207 @Override
208 public void remove() {
209 Jcr.remove(getJcrNode());
210 }
211
212 @Override
213 protected void removeAttr(QName key) {
214 Property property = Jcr.getProperty(getJcrNode(), key.toString());
215 if (property != null) {
216 try {
217 property.remove();
218 } catch (RepositoryException e) {
219 throw new JcrException("Cannot remove property " + key + " from " + getJcrNode(), e);
220 }
221 }
222
223 }
224
225 boolean exists() {
226 try {
227 return provider.getJcrSession(getSession(), jcrWorkspace).itemExists(jcrPath);
228 } catch (RepositoryException e) {
229 throw new JcrException("Cannot check whether " + jcrPath + " exists", e);
230 }
231 }
232
233 /*
234 * ADAPTERS
235 */
236 @SuppressWarnings("unchecked")
237 public <A> A adapt(Class<A> clss) {
238 if (Source.class.isAssignableFrom(clss)) {
239 // try {
240 PipedInputStream in = new PipedInputStream();
241
242 ForkJoinPool.commonPool().execute(() -> {
243 try (PipedOutputStream out = new PipedOutputStream(in)) {
244 provider.getJcrSession(getSession(), jcrWorkspace).exportDocumentView(jcrPath, out, true, false);
245 out.flush();
246 } catch (IOException | RepositoryException e) {
247 throw new RuntimeException("Cannot export " + jcrPath + " in workspace " + jcrWorkspace, e);
248 }
249
250 });
251 return (A) new StreamSource(in);
252 // } catch (IOException e) {
253 // throw new RuntimeException("Cannot adapt " + JcrContent.this + " to " + clss, e);
254 // }
255 } else
256
257 return super.adapt(clss);
258 }
259
260 @SuppressWarnings("unchecked")
261 @Override
262 public <C extends Closeable> C open(Class<C> clss) throws IOException, IllegalArgumentException {
263 if (InputStream.class.isAssignableFrom(clss)) {
264 Node node = getJcrNode();
265 if (Jcr.isNodeType(node, NodeType.NT_FILE)) {
266 try {
267 return (C) JcrUtils.getFileAsStream(node);
268 } catch (RepositoryException e) {
269 throw new JcrException("Cannot open " + jcrPath + " in workspace " + jcrWorkspace, e);
270 }
271 }
272 }
273 return super.open(clss);
274 }
275
276 @Override
277 public ContentProvider getProvider() {
278 return provider;
279 }
280
281 @Override
282 public String getSessionLocalId() {
283 try {
284 return getJcrNode().getIdentifier();
285 } catch (RepositoryException e) {
286 throw new JcrException("Cannot get identifier for " + getJcrNode(), e);
287 }
288 }
289
290 /*
291 * TYPING
292 */
293 @Override
294 public List<QName> getContentClasses() {
295 try {
296 // Node node = getJcrNode();
297 // List<QName> res = new ArrayList<>();
298 // res.add(nodeTypeToQName(node.getPrimaryNodeType()));
299 // for (NodeType mixin : node.getMixinNodeTypes()) {
300 // res.add(nodeTypeToQName(mixin));
301 // }
302 // return res;
303 Node context = getJcrNode();
304
305 List<QName> res = new ArrayList<>();
306 // primary node type
307 NodeType primaryType = context.getPrimaryNodeType();
308 res.add(nodeTypeToQName(primaryType));
309
310 Set<QName> secondaryTypes = new TreeSet<>(NamespaceUtils.QNAME_COMPARATOR);
311 for (NodeType mixinType : context.getMixinNodeTypes()) {
312 secondaryTypes.add(nodeTypeToQName(mixinType));
313 }
314 for (NodeType superType : primaryType.getDeclaredSupertypes()) {
315 secondaryTypes.add(nodeTypeToQName(superType));
316 }
317 // mixins
318 for (NodeType mixinType : context.getMixinNodeTypes()) {
319 for (NodeType superType : mixinType.getDeclaredSupertypes()) {
320 secondaryTypes.add(nodeTypeToQName(superType));
321 }
322 }
323 // // entity type
324 // if (context.isNodeType(EntityType.entity.get())) {
325 // if (context.hasProperty(EntityNames.ENTITY_TYPE)) {
326 // String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString();
327 // if (byType.containsKey(entityTypeName)) {
328 // types.add(entityTypeName);
329 // }
330 // }
331 // }
332 res.addAll(secondaryTypes);
333 return res;
334 } catch (RepositoryException e) {
335 throw new JcrException("Cannot list node types from " + getJcrNode(), e);
336 }
337 }
338
339 private QName nodeTypeToQName(NodeType nodeType) {
340 String name = nodeType.getName();
341 return QName.valueOf(name);
342 }
343
344 /*
345 * STATIC UTLITIES
346 */
347 public static Content nodeToContent(Node node) {
348 if (node == null)
349 return null;
350 try {
351 ProvidedSession contentSession = (ProvidedSession) node.getSession()
352 .getAttribute(ProvidedSession.class.getName());
353 if (contentSession == null)
354 throw new IllegalArgumentException(
355 "Cannot adapt " + node + " to content, because it was not loaded from a content session");
356 return contentSession.get(CmsConstants.SYS_WORKSPACE + node.getPath());
357 } catch (RepositoryException e) {
358 throw new JcrException("Cannot adapt " + node + " to a content", e);
359 }
360 }
361
362 }