]> git.argeo.org Git - gpl/argeo-jcr.git/blob - org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java
Improve writing to a JCR Content
[gpl/argeo-jcr.git] / 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.OutputStream;
7 import java.io.PipedInputStream;
8 import java.io.PipedOutputStream;
9 import java.time.Instant;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Calendar;
13 import java.util.Date;
14 import java.util.GregorianCalendar;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Objects;
20 import java.util.Optional;
21 import java.util.Set;
22 import java.util.TreeSet;
23 import java.util.UUID;
24 import java.util.concurrent.ForkJoinPool;
25
26 import javax.jcr.Binary;
27 import javax.jcr.Node;
28 import javax.jcr.NodeIterator;
29 import javax.jcr.Property;
30 import javax.jcr.PropertyIterator;
31 import javax.jcr.PropertyType;
32 import javax.jcr.RepositoryException;
33 import javax.jcr.Session;
34 import javax.jcr.Value;
35 import javax.jcr.ValueFactory;
36 import javax.jcr.nodetype.NodeType;
37 import javax.jcr.nodetype.NodeTypeManager;
38 import javax.xml.namespace.QName;
39 import javax.xml.transform.Source;
40 import javax.xml.transform.stream.StreamSource;
41
42 import org.argeo.api.acr.Content;
43 import org.argeo.api.acr.CrAttributeType;
44 import org.argeo.api.acr.DName;
45 import org.argeo.api.acr.NamespaceUtils;
46 import org.argeo.api.acr.spi.ProvidedSession;
47 import org.argeo.api.cms.CmsConstants;
48 import org.argeo.cms.acr.AbstractContent;
49 import org.argeo.cms.acr.ContentUtils;
50 import org.argeo.cms.util.AsyncPipedOutputStream;
51 import org.argeo.jcr.Jcr;
52 import org.argeo.jcr.JcrException;
53 import org.argeo.jcr.JcrUtils;
54 import org.argeo.jcr.JcrxApi;
55
56 /** A JCR {@link Node} accessed as {@link Content}. */
57 public class JcrContent extends AbstractContent {
58 private JcrContentProvider provider;
59
60 private String jcrWorkspace;
61 private String jcrPath;
62
63 private final boolean isMountBase;
64
65 /* OPTIMISATIONS */
66 /**
67 * While we want to support thread-safe access, it is very likely that only
68 * thread and only one sesssion will be used (typically from a single-threaded
69 * UI). We therefore cache was long as the same thread is calling.
70 */
71 private Thread lastRetrievingThread = null;
72 private Node cachedNode = null;
73 private boolean caching = true;
74
75 protected JcrContent(ProvidedSession session, JcrContentProvider provider, String jcrWorkspace, String jcrPath) {
76 super(session);
77 this.provider = provider;
78 this.jcrWorkspace = jcrWorkspace;
79 this.jcrPath = jcrPath;
80
81 this.isMountBase = ContentUtils.SLASH_STRING.equals(jcrPath);
82 }
83
84 /*
85 * READ
86 */
87
88 @Override
89 public QName getName() {
90 String name = Jcr.getName(getJcrNode());
91 if (name.equals("")) {// root
92 String mountPath = provider.getMountPath();
93 name = ContentUtils.getParentPath(mountPath)[1];
94 // name = Jcr.getWorkspaceName(getJcrNode());
95 }
96 return NamespaceUtils.parsePrefixedName(provider, name);
97 }
98
99 @SuppressWarnings("unchecked")
100 @Override
101 public <A> Optional<A> get(QName key, Class<A> clss) {
102 try {
103 Node node = getJcrNode();
104 if (DName.creationdate.equals(key))
105 key = JcrName.created.qName();
106 else if (DName.getlastmodified.equals(key))
107 key = JcrName.lastModified.qName();
108 else if (DName.getcontenttype.equals(key)) {
109 String contentType = null;
110 if (node.isNodeType(NodeType.NT_FILE)) {
111 Node content = node.getNode(Node.JCR_CONTENT);
112 if (content.isNodeType(NodeType.MIX_MIMETYPE)) {
113 contentType = content.getProperty(Property.JCR_MIMETYPE).getString();
114 if (content.hasProperty(Property.JCR_ENCODING))
115 contentType = contentType + ";encoding="
116 + content.getProperty(Property.JCR_ENCODING).getString();
117 }
118 }
119 if (contentType == null)
120 contentType = "application/octet-stream";
121 return CrAttributeType.cast(clss, contentType);
122 } else if (DName.checkedOut.equals(key)) {
123 if (!node.hasProperty(Property.JCR_IS_CHECKED_OUT))
124 return Optional.empty();
125 boolean isCheckedOut = node.getProperty(Property.JCR_IS_CHECKED_OUT).getBoolean();
126 if (!isCheckedOut)
127 return Optional.empty();
128 // FIXME return URI
129 return (Optional<A>) Optional.of(new Object());
130 } else if (DName.checkedIn.equals(key)) {
131 if (!node.hasProperty(Property.JCR_IS_CHECKED_OUT))
132 return Optional.empty();
133 boolean isCheckedOut = node.getProperty(Property.JCR_IS_CHECKED_OUT).getBoolean();
134 if (isCheckedOut)
135 return Optional.empty();
136 // FIXME return URI
137 return (Optional<A>) Optional.of(new Object());
138 }
139
140 Object value = get(node, key.toString());
141 if (value instanceof List<?> lst)
142 return Optional.of((A) lst);
143 // TODO check other collections?
144 return CrAttributeType.cast(clss, value);
145 } catch (RepositoryException e) {
146 throw new JcrException(e);
147 }
148 }
149
150 @Override
151 public Iterator<Content> iterator() {
152 try {
153 return new JcrContentIterator(getJcrNode().getNodes());
154 } catch (RepositoryException e) {
155 throw new JcrException("Cannot list children of " + getJcrNode(), e);
156 }
157 }
158
159 @Override
160 protected Iterable<QName> keys() {
161 try {
162 Node node = getJcrNode();
163 Set<QName> keys = new HashSet<>();
164 for (PropertyIterator propertyIterator = node.getProperties(); propertyIterator.hasNext();) {
165 Property property = propertyIterator.nextProperty();
166 QName name = NamespaceUtils.parsePrefixedName(provider, property.getName());
167
168 // TODO convert standard names
169 if (property.getName().equals(Property.JCR_CREATED))
170 name = DName.creationdate.qName();
171 else if (property.getName().equals(Property.JCR_LAST_MODIFIED))
172 name = DName.getlastmodified.qName();
173 else if (property.getName().equals(Property.JCR_MIMETYPE))
174 name = DName.getcontenttype.qName();
175 else if (property.getName().equals(Property.JCR_IS_CHECKED_OUT)) {
176 boolean isCheckedOut = node.getProperty(Property.JCR_IS_CHECKED_OUT).getBoolean();
177 name = isCheckedOut ? DName.checkedOut.qName() : DName.checkedIn.qName();
178 }
179
180 // TODO skip technical properties
181 keys.add(name);
182 }
183 return keys;
184 } catch (RepositoryException e) {
185 throw new JcrException("Cannot list properties of " + getJcrNode(), e);
186 }
187 }
188
189 /** Cast to a standard Java object. */
190 static Object get(Node node, String property) {
191 try {
192 if (!node.hasProperty(property))
193 return null;
194 Property p = node.getProperty(property);
195 if (p.isMultiple()) {
196 Value[] values = p.getValues();
197 List<Object> lst = new ArrayList<>();
198 for (Value value : values) {
199 lst.add(convertSingleValue(value));
200 }
201 return lst;
202 } else {
203 Value value = node.getProperty(property).getValue();
204 return convertSingleValue(value);
205 }
206 } catch (RepositoryException e) {
207 throw new JcrException("Cannot cast value from " + property + " of " + node, e);
208 }
209 }
210
211 @Override
212 public boolean isMultiple(QName key) {
213 Node node = getJcrNode();
214 String p = NamespaceUtils.toFullyQualified(key);
215 try {
216 if (node.hasProperty(p)) {
217 Property property = node.getProperty(p);
218 return property.isMultiple();
219 } else {
220 return false;
221 }
222 } catch (RepositoryException e) {
223 throw new JcrException(
224 "Cannot check multiplicity of property " + p + " of " + jcrPath + " in " + jcrWorkspace, e);
225 }
226 }
227
228 @Override
229 public String getPath() {
230 try {
231 // Note: it is important to to use the default way (recursing through parents),
232 // since the session may not have access to parent nodes
233 return Content.ROOT_PATH + jcrWorkspace + getJcrNode().getPath();
234 } catch (RepositoryException e) {
235 throw new JcrException("Cannot get depth of " + getJcrNode(), e);
236 }
237 }
238
239 @Override
240 public int getDepth() {
241 try {
242 return getJcrNode().getDepth() + 1;
243 } catch (RepositoryException e) {
244 throw new JcrException("Cannot get depth of " + getJcrNode(), e);
245 }
246 }
247
248 @Override
249 public Content getParent() {
250 if (isMountBase) {
251 String mountPath = provider.getMountPath();
252 if (mountPath == null || mountPath.equals("/"))
253 return null;
254 String[] parent = ContentUtils.getParentPath(mountPath);
255 return getSession().get(parent[0]);
256 }
257 // if (Jcr.isRoot(getJcrNode())) // root
258 // return null;
259 return new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getParentPath(getJcrNode()));
260 }
261
262 @Override
263 public int getSiblingIndex() {
264 return Jcr.getIndex(getJcrNode());
265 }
266
267 @Override
268 public String getText() {
269 return JcrxApi.getXmlValue(getJcrNode());
270 }
271
272 /*
273 * MAP OPTIMISATIONS
274 */
275 @Override
276 public boolean containsKey(Object key) {
277 return Jcr.hasProperty(getJcrNode(), key.toString());
278 }
279
280 /*
281 * WRITE
282 */
283
284 protected Node openForEdit() {
285 Node node = getProvider().openForEdit(getSession(), jcrWorkspace, jcrPath);
286 getSession().notifyModification(this);
287 return node;
288 }
289
290 @Override
291 public Content add(QName name, QName... classes) {
292 try {
293 Node child;
294 if (classes.length > 0) {
295 QName primaryType = classes[0];
296 Node node = openForEdit();
297 child = Jcr.addNode(node, name.toString(), primaryType.toString());
298
299 for (int i = 1; i < classes.length; i++)
300 child.addMixin(classes[i].toString());
301
302 if (NtType.file.qName().equals(primaryType)) {
303 // TODO optimise when we have a proper save mechanism
304 child.addNode(Node.JCR_CONTENT, NodeType.NT_UNSTRUCTURED);
305 // Binary binary;
306 // try (InputStream in = new ByteArrayInputStream(new byte[0])) {
307 // binary = content.getSession().getValueFactory().createBinary(in);
308 // content.setProperty(Property.JCR_DATA, binary);
309 // } catch (IOException e) {
310 // throw new UncheckedIOException(e);
311 // }
312 child.getSession().save();
313 }
314 } else {
315 child = Jcr.addNode(getJcrNode(), name.toString(), NodeType.NT_UNSTRUCTURED);
316 }
317 return new JcrContent(getSession(), provider, jcrWorkspace, child.getPath());
318 } catch (RepositoryException e) {
319 throw new JcrException("Cannot add child to " + jcrPath + " in " + jcrWorkspace, e);
320 }
321 }
322
323 @Override
324 public Content add(QName name, Map<QName, Object> attrs, QName... classes) {
325 if (attrs.containsKey(DName.getcontenttype.qName())) {
326 List<QName> lst = new ArrayList<>(Arrays.asList(classes));
327 lst.add(0, NtType.file.qName());
328 classes = lst.toArray(new QName[lst.size()]);
329 }
330 if (attrs.containsKey(DName.collection.qName())) {
331 List<QName> lst = Arrays.asList(classes);
332 lst.add(0, NtType.folder.qName());
333 classes = lst.toArray(new QName[lst.size()]);
334 }
335 Content child = add(name, classes);
336 child.putAll(attrs);
337 return child;
338 }
339
340 @Override
341 public void remove() {
342 Node node = openForEdit();
343 Jcr.remove(node);
344 saveJcrSession();
345 }
346
347 private void saveJcrSession() {
348 try {
349 getJcrSession().save();
350 } catch (RepositoryException e) {
351 throw new JcrException("Cannot persist " + jcrPath + " in " + jcrWorkspace, e);
352 }
353 }
354
355 @Override
356 protected void removeAttr(QName key) {
357 Node node = openForEdit();
358 Property property = Jcr.getProperty(node, key.toString());
359 if (property != null) {
360 try {
361 property.remove();
362 } catch (RepositoryException e) {
363 throw new JcrException("Cannot remove property " + key + " from " + getJcrNode(), e);
364 }
365 }
366 saveJcrSession();
367 }
368
369 @Override
370 public Object put(QName key, Object value) {
371 Objects.requireNonNull(value, "Value cannot be null");
372 Node node = openForEdit();
373 try {
374 if (DName.checkedIn.equals(key) || DName.checkedOut.equals(key))
375 throw new IllegalArgumentException(
376 key + " cannot be set, use the openForEdit/freeze methods of the related content provider.");
377
378 Object old = null;
379 String property;
380 if (DName.creationdate.equals(key))
381 property = Property.JCR_CREATED;
382 else if (DName.getlastmodified.equals(key))
383 property = Property.JCR_LAST_MODIFIED;
384 else if (DName.getcontenttype.equals(key)) {
385 if (!node.isNodeType(NodeType.NT_FILE))
386 throw new IllegalStateException(DName.getcontenttype + " can only be set on a file");
387 Node content = node.getNode(Node.JCR_CONTENT);
388 old = Jcr.get(content, Property.JCR_MIMETYPE);
389 if (old != null && Jcr.hasProperty(content, Property.JCR_ENCODING))
390 old = old + ";encoding=" + Jcr.get(content, Property.JCR_ENCODING);
391 String[] str = value.toString().split(";");
392 String mimeType = str[0].trim();
393 String encoding = null;
394 if (str.length > 1) {
395 value = str[0].trim();
396 String[] eq = str[1].split("=");
397 assert eq.length == 2;
398 if ("encoding".equals(eq[0].trim()))
399 encoding = eq[1];
400 }
401 content.setProperty(Property.JCR_MIMETYPE, mimeType);
402 if (encoding != null)
403 content.setProperty(Property.JCR_ENCODING, encoding);
404 property = null;
405 } else
406 property = NamespaceUtils.toFullyQualified(key);
407
408 if (property != null) {
409 if (node.hasProperty(property)) {
410 old = convertSingleValue(node.getProperty(property).getValue());
411 }
412 Value newValue = convertSingleObject(node.getSession().getValueFactory(), value);
413 node.setProperty(property, newValue);
414 }
415 // FIXME proper edition
416 saveJcrSession();
417 return old;
418 } catch (RepositoryException e) {
419 throw new JcrException("Cannot set property " + key + " on " + jcrPath + " in " + jcrWorkspace, e);
420 }
421 }
422
423 @Override
424 public void addContentClasses(QName... contentClass) throws IllegalArgumentException, JcrException {
425 try {
426 Node node = openForEdit();
427 NodeTypeManager ntm = node.getSession().getWorkspace().getNodeTypeManager();
428 List<NodeType> nodeTypes = new ArrayList<>();
429 for (QName clss : contentClass) {
430 NodeType nodeType = ntm.getNodeType(NamespaceUtils.toFullyQualified(clss));
431 if (!nodeType.isMixin())
432 throw new IllegalArgumentException(clss + " is not a mixin");
433 nodeTypes.add(nodeType);
434 }
435 for (NodeType nodeType : nodeTypes) {
436 node.addMixin(nodeType.getName());
437 }
438 // FIXME proper edition
439 saveJcrSession();
440 } catch (RepositoryException e) {
441 throw new JcrException(
442 "Cannot add content classes " + contentClass + " to " + jcrPath + " in " + jcrWorkspace, e);
443 }
444 }
445
446 /*
447 * ACCESS
448 */
449 protected boolean exists() {
450 try {
451 return getJcrSession().itemExists(jcrPath);
452 } catch (RepositoryException e) {
453 throw new JcrException("Cannot check whether " + jcrPath + " exists", e);
454 }
455 }
456
457 @Override
458 public boolean isParentAccessible() {
459 String jcrParentPath = ContentUtils.getParentPath(jcrPath)[0];
460 if ("".equals(jcrParentPath)) // JCR root node
461 jcrParentPath = ContentUtils.SLASH_STRING;
462 try {
463 return getJcrSession().hasPermission(jcrParentPath, Session.ACTION_READ);
464 } catch (RepositoryException e) {
465 throw new JcrException("Cannot check whether parent " + jcrParentPath + " is accessible", e);
466 }
467 }
468
469 /*
470 * ADAPTERS
471 */
472 @SuppressWarnings("unchecked")
473 public <A> A adapt(Class<A> clss) {
474 if (Node.class.isAssignableFrom(clss)) {
475 return (A) getJcrNode();
476 } else if (Source.class.isAssignableFrom(clss)) {
477 // try {
478 PipedOutputStream out = new PipedOutputStream();
479 PipedInputStream in;
480 try {
481 in = new PipedInputStream(out);
482 } catch (IOException e) {
483 throw new RuntimeException("Cannot export " + jcrPath + " in workspace " + jcrWorkspace, e);
484 }
485
486 ForkJoinPool.commonPool().execute(() -> {
487 // try (PipedOutputStream out = new PipedOutputStream(in)) {
488 try {
489 getJcrSession().exportDocumentView(jcrPath, out, true, false);
490 out.flush();
491 out.close();
492 } catch (IOException | RepositoryException e) {
493 throw new RuntimeException("Cannot export " + jcrPath + " in workspace " + jcrWorkspace, e);
494 }
495
496 });
497 return (A) new StreamSource(in);
498 // } catch (IOException e) {
499 // throw new RuntimeException("Cannot adapt " + JcrContent.this + " to " + clss, e);
500 // }
501 } else {
502 return super.adapt(clss);
503 }
504 }
505
506 @SuppressWarnings("unchecked")
507 @Override
508 public <C extends Closeable> C open(Class<C> clss) throws IOException, IllegalArgumentException {
509 try {
510 if (InputStream.class.isAssignableFrom(clss)) {
511 Node node = getJcrNode();
512 if (Jcr.isNodeType(node, NodeType.NT_FILE)) {
513 return (C) JcrUtils.getFileAsStream(node);
514 }
515 } else if (OutputStream.class.isAssignableFrom(clss)) {
516 Node node = getJcrNode();
517 if (Jcr.isNodeType(node, NodeType.NT_FILE)) {
518 Node content = node.getNode(Node.JCR_CONTENT);
519 AsyncPipedOutputStream out = new AsyncPipedOutputStream();
520
521 ValueFactory valueFactory = getJcrSession().getValueFactory();
522 out.asyncRead((in) -> {
523 try {
524 Binary binary = valueFactory.createBinary(in);
525 content.setProperty(Property.JCR_DATA, binary);
526 saveJcrSession();
527 // System.out.println("Binary written");
528 } catch (RepositoryException e) {
529 throw new JcrException(
530 "Cannot create binary in " + jcrPath + " in workspace " + jcrWorkspace, e);
531 }
532 });
533 //
534 // PipedInputStream in = new PipedInputStream() {
535 //
536 // @Override
537 // public void close() throws IOException {
538 // System.out.println("Piped IN closing...");
539 // super.close();
540 // }
541 // };
542 // CompletableFuture<Void> done = CompletableFuture.runAsync(() -> {
543 // try {
544 // Binary binary = valueFactory.createBinary(in);
545 // content.setProperty(Property.JCR_DATA, binary);
546 // saveJcrSession();
547 // } catch (RepositoryException e) {
548 // throw new JcrException(
549 // "Cannot create binary in " + jcrPath + " in workspace " + jcrWorkspace, e);
550 // }
551 // });
552 // PipedOutputStream out = new PipedOutputStream(in) {
553 // @Override
554 // public void close() throws IOException {
555 // super.flush();
556 // System.out.println("Piped OUT closing...");
557 // super.close();
558 // done.join();
559 // }
560 // };
561 return (C) out;
562 }
563 }
564 } catch (RepositoryException e) {
565 throw new JcrException("Cannot open " + jcrPath + " in workspace " + jcrWorkspace, e);
566 }
567 return super.open(clss);
568 }
569
570 @Override
571 public JcrContentProvider getProvider() {
572 return provider;
573 }
574
575 @Override
576 public String getSessionLocalId() {
577 try {
578 return getJcrNode().getIdentifier();
579 } catch (RepositoryException e) {
580 throw new JcrException("Cannot get identifier for " + getJcrNode(), e);
581 }
582 }
583
584 /*
585 * TYPING
586 */
587
588 static Object convertSingleValue(Value value) throws JcrException, IllegalArgumentException {
589 try {
590 switch (value.getType()) {
591 case PropertyType.STRING:
592 return value.getString();
593 case PropertyType.DOUBLE:
594 return (Double) value.getDouble();
595 case PropertyType.LONG:
596 return (Long) value.getLong();
597 case PropertyType.BOOLEAN:
598 return (Boolean) value.getBoolean();
599 case PropertyType.DATE:
600 Calendar calendar = value.getDate();
601 return calendar.toInstant();
602 case PropertyType.BINARY:
603 throw new IllegalArgumentException("Binary is not supported as an attribute");
604 default:
605 return value.getString();
606 }
607 } catch (RepositoryException e) {
608 throw new JcrException("Cannot convert " + value + " to an object.", e);
609 }
610 }
611
612 static Value convertSingleObject(ValueFactory factory, Object value) {
613 if (value instanceof String string) {
614 return factory.createValue(string);
615 } else if (value instanceof Double dbl) {
616 return factory.createValue(dbl);
617 } else if (value instanceof Float flt) {
618 return factory.createValue(flt);
619 } else if (value instanceof Long lng) {
620 return factory.createValue(lng);
621 } else if (value instanceof Integer intg) {
622 return factory.createValue(intg);
623 } else if (value instanceof Boolean bool) {
624 return factory.createValue(bool);
625 } else if (value instanceof Instant instant) {
626 GregorianCalendar calendar = new GregorianCalendar();
627 calendar.setTime(Date.from(instant));
628 return factory.createValue(calendar);
629 } else {
630 // TODO or use String by default?
631 throw new IllegalArgumentException("Unsupported value " + value.getClass());
632 }
633 }
634
635 @Override
636 public Class<?> getType(QName key) {
637 Node node = getJcrNode();
638 String p = NamespaceUtils.toFullyQualified(key);
639 try {
640 if (node.hasProperty(p)) {
641 Property property = node.getProperty(p);
642 return switch (property.getType()) {
643 case PropertyType.STRING:
644 case PropertyType.NAME:
645 case PropertyType.PATH:
646 case PropertyType.DECIMAL:
647 yield String.class;
648 case PropertyType.LONG:
649 yield Long.class;
650 case PropertyType.DOUBLE:
651 yield Double.class;
652 case PropertyType.BOOLEAN:
653 yield Boolean.class;
654 case PropertyType.DATE:
655 yield Instant.class;
656 case PropertyType.WEAKREFERENCE:
657 case PropertyType.REFERENCE:
658 yield UUID.class;
659 default:
660 yield Object.class;
661 };
662 } else {
663 // TODO does it make sense?
664 return Object.class;
665 }
666 } catch (RepositoryException e) {
667 throw new JcrException("Cannot get type of property " + p + " of " + jcrPath + " in " + jcrWorkspace, e);
668 }
669 }
670
671 @Override
672 public List<QName> getContentClasses() {
673 try {
674 Node context = getJcrNode();
675
676 List<QName> res = new ArrayList<>();
677 // primary node type
678 NodeType primaryType = context.getPrimaryNodeType();
679 res.add(nodeTypeToQName(primaryType));
680 if (primaryType.getName().equals(NodeType.NT_FOLDER))
681 res.add(DName.collection.qName());
682
683 Set<QName> secondaryTypes = new TreeSet<>(NamespaceUtils.QNAME_COMPARATOR);
684 for (NodeType mixinType : context.getMixinNodeTypes()) {
685 secondaryTypes.add(nodeTypeToQName(mixinType));
686 }
687 for (NodeType superType : primaryType.getDeclaredSupertypes()) {
688 secondaryTypes.add(nodeTypeToQName(superType));
689 }
690 // mixins
691 for (NodeType mixinType : context.getMixinNodeTypes()) {
692 for (NodeType superType : mixinType.getDeclaredSupertypes()) {
693 secondaryTypes.add(nodeTypeToQName(superType));
694 }
695 }
696 res.addAll(secondaryTypes);
697 return res;
698 } catch (RepositoryException e) {
699 throw new JcrException("Cannot list node types from " + getJcrNode(), e);
700 }
701 }
702
703 private QName nodeTypeToQName(NodeType nodeType) {
704 String name = nodeType.getName();
705 return NamespaceUtils.parsePrefixedName(provider, name);
706 // return QName.valueOf(name);
707 }
708
709 /*
710 * COMMON UTILITIES
711 */
712 protected Session getJcrSession() {
713 return provider.getJcrSession(getSession(), jcrWorkspace);
714 }
715
716 protected Node getJcrNode() {
717 try {
718 if (caching) {
719 synchronized (this) {
720 if (lastRetrievingThread != Thread.currentThread()) {
721 cachedNode = getJcrSession().getNode(jcrPath);
722 lastRetrievingThread = Thread.currentThread();
723 }
724 return cachedNode;
725 }
726 } else {
727 return getJcrSession().getNode(jcrPath);
728 }
729 } catch (RepositoryException e) {
730 throw new JcrException("Cannot retrieve " + jcrPath + " from workspace " + jcrWorkspace, e);
731 }
732 }
733
734 /*
735 * STATIC UTLITIES
736 */
737 public static Content nodeToContent(Node node) {
738 if (node == null)
739 return null;
740 try {
741 ProvidedSession contentSession = (ProvidedSession) node.getSession()
742 .getAttribute(ProvidedSession.class.getName());
743 if (contentSession == null)
744 throw new IllegalArgumentException(
745 "Cannot adapt " + node + " to content, because it was not loaded from a content session");
746 return contentSession.get(Content.ROOT_PATH + CmsConstants.SYS_WORKSPACE + node.getPath());
747 } catch (RepositoryException e) {
748 throw new JcrException("Cannot adapt " + node + " to a content", e);
749 }
750 }
751
752 /*
753 * CONTENT ITERATOR
754 */
755
756 class JcrContentIterator implements Iterator<Content> {
757 private final NodeIterator nodeIterator;
758 // we keep track in order to be able to delete it
759 private JcrContent current = null;
760
761 protected JcrContentIterator(NodeIterator nodeIterator) {
762 this.nodeIterator = nodeIterator;
763 }
764
765 @Override
766 public boolean hasNext() {
767 return nodeIterator.hasNext();
768 }
769
770 @Override
771 public Content next() {
772 current = new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getPath(nodeIterator.nextNode()));
773 return current;
774 }
775
776 @Override
777 public void remove() {
778 if (current != null) {
779 Jcr.remove(current.getJcrNode());
780 }
781 }
782
783 }
784
785 }