3 import java
.math
.BigDecimal
;
4 import java
.time
.Instant
;
5 import java
.util
.ArrayList
;
6 import java
.util
.Calendar
;
7 import java
.util
.Collections
;
9 import java
.util
.GregorianCalendar
;
10 import java
.util
.Iterator
;
11 import java
.util
.List
;
13 import javax
.jcr
.Binary
;
14 import javax
.jcr
.ItemNotFoundException
;
15 import javax
.jcr
.Node
;
16 import javax
.jcr
.NodeIterator
;
17 import javax
.jcr
.Property
;
18 import javax
.jcr
.PropertyType
;
19 import javax
.jcr
.Repository
;
20 import javax
.jcr
.RepositoryException
;
21 import javax
.jcr
.Session
;
22 import javax
.jcr
.Value
;
23 import javax
.jcr
.Workspace
;
24 import javax
.jcr
.nodetype
.NodeType
;
25 import javax
.jcr
.security
.Privilege
;
26 import javax
.jcr
.version
.Version
;
27 import javax
.jcr
.version
.VersionHistory
;
28 import javax
.jcr
.version
.VersionIterator
;
29 import javax
.jcr
.version
.VersionManager
;
32 * Utility class whose purpose is to make using JCR less verbose by
33 * systematically using unchecked exceptions and returning <code>null</code>
34 * when something is not found. This is especially useful when writing user
35 * interfaces (such as with SWT) where listeners and callbacks expect unchecked
36 * exceptions. Loosely inspired by Java's <code>Files</code> singleton.
41 * @see Node#isNodeType(String)
42 * @throws IllegalStateException caused by {@link RepositoryException}
44 public static boolean isNodeType(Node node
, String nodeTypeName
) {
46 return node
.isNodeType(nodeTypeName
);
47 } catch (RepositoryException e
) {
48 throw new IllegalStateException("Cannot get whether " + node
+ " is of type " + nodeTypeName
, e
);
53 * @see Node#hasNodes()
54 * @throws IllegalStateException caused by {@link RepositoryException}
56 public static boolean hasNodes(Node node
) {
58 return node
.hasNodes();
59 } catch (RepositoryException e
) {
60 throw new IllegalStateException("Cannot get whether " + node
+ " has children.", e
);
65 * @see Node#getParent()
66 * @throws IllegalStateException caused by {@link RepositoryException}
68 public static Node
getParent(Node node
) {
70 return isRoot(node
) ?
null : node
.getParent();
71 } catch (RepositoryException e
) {
72 throw new IllegalStateException("Cannot get parent of " + node
, e
);
77 * Whether this node is the root node.
79 * @throws IllegalStateException caused by {@link RepositoryException}
81 public static boolean isRoot(Node node
) {
83 return node
.getDepth() == 0;
84 } catch (RepositoryException e
) {
85 throw new IllegalStateException("Cannot get depth of " + node
, e
);
91 * @throws IllegalStateException caused by {@link RepositoryException}
93 public static String
getPath(Node node
) {
95 return node
.getPath();
96 } catch (RepositoryException e
) {
97 throw new IllegalStateException("Cannot get path of " + node
, e
);
102 * @see Node#getSession()
103 * @see Session#getWorkspace()
104 * @see Workspace#getName()
106 public static String
getWorkspaceName(Node node
) {
107 return session(node
).getWorkspace().getName();
111 * @see Node#getIdentifier()
112 * @throws IllegalStateException caused by {@link RepositoryException}
114 public static String
getIdentifier(Node node
) {
116 return node
.getIdentifier();
117 } catch (RepositoryException e
) {
118 throw new IllegalStateException("Cannot get identifier of " + node
, e
);
123 * @see Node#getName()
124 * @throws IllegalStateException caused by {@link RepositoryException}
126 public static String
getName(Node node
) {
128 return node
.getName();
129 } catch (RepositoryException e
) {
130 throw new IllegalStateException("Cannot get name of " + node
, e
);
135 * If node has mixin {@link NodeType#MIX_TITLE}, return
136 * {@link Property#JCR_TITLE}, otherwise return {@link #getName(Node)}.
138 public static String
getTitle(Node node
) {
139 if (Jcr
.isNodeType(node
, NodeType
.MIX_TITLE
))
140 return get(node
, Property
.JCR_TITLE
);
142 return Jcr
.getName(node
);
145 /** Accesses a {@link NodeIterator} as an {@link Iterable}. */
146 @SuppressWarnings("unchecked")
147 public static Iterable
<Node
> iterate(NodeIterator nodeIterator
) {
148 return new Iterable
<Node
>() {
151 public Iterator
<Node
> iterator() {
158 * @return the children as an {@link Iterable} for use in for-each llops.
159 * @see Node#getNodes()
160 * @throws IllegalStateException caused by {@link RepositoryException}
162 public static Iterable
<Node
> nodes(Node node
) {
164 return iterate(node
.getNodes());
165 } catch (RepositoryException e
) {
166 throw new IllegalStateException("Cannot get children of " + node
, e
);
171 * @return the children as a (possibly empty) {@link List}.
172 * @see Node#getNodes()
173 * @throws IllegalStateException caused by {@link RepositoryException}
175 public static List
<Node
> getNodes(Node node
) {
176 List
<Node
> nodes
= new ArrayList
<>();
178 if (node
.hasNodes()) {
179 NodeIterator nit
= node
.getNodes();
180 while (nit
.hasNext())
181 nodes
.add(nit
.nextNode());
185 } catch (RepositoryException e
) {
186 throw new IllegalStateException("Cannot get children of " + node
, e
);
191 * @return the child or <code>null</node> if not found
192 * @see Node#getNode(String)
193 * @throws IllegalStateException caused by {@link RepositoryException}
195 public static Node
getNode(Node node
, String child
) {
197 if (node
.hasNode(child
))
198 return node
.getNode(child
);
201 } catch (RepositoryException e
) {
202 throw new IllegalStateException("Cannot get child of " + node
, e
);
207 * @return the node at this path or <code>null</node> if not found
208 * @see Session#getNode(String)
209 * @throws IllegalStateException caused by {@link RepositoryException}
211 public static Node
getNode(Session session
, String path
) {
213 if (session
.nodeExists(path
))
214 return session
.getNode(path
);
217 } catch (RepositoryException e
) {
218 throw new IllegalStateException("Cannot get node " + path
, e
);
223 * @return the node with htis id or <code>null</node> if not found
224 * @see Session#getNodeByIdentifier(String)
225 * @throws IllegalStateException caused by {@link RepositoryException}
227 public static Node
getNodeById(Session session
, String id
) {
229 return session
.getNodeByIdentifier(id
);
230 } catch (ItemNotFoundException e
) {
232 } catch (RepositoryException e
) {
233 throw new IllegalStateException("Cannot get node with id " + id
, e
);
238 * Set a property to the given value, or remove it if the value is
241 * @throws IllegalStateException caused by {@link RepositoryException}
243 public static void set(Node node
, String property
, Object value
) {
245 if (!node
.hasProperty(property
))
246 throw new IllegalArgumentException("No property " + property
+ " in " + node
);
247 Property prop
= node
.getProperty(property
);
253 if (value
instanceof String
)
254 prop
.setValue((String
) value
);
255 else if (value
instanceof Long
)
256 prop
.setValue((Long
) value
);
257 else if (value
instanceof Double
)
258 prop
.setValue((Double
) value
);
259 else if (value
instanceof Calendar
)
260 prop
.setValue((Calendar
) value
);
261 else if (value
instanceof BigDecimal
)
262 prop
.setValue((BigDecimal
) value
);
263 else if (value
instanceof Boolean
)
264 prop
.setValue((Boolean
) value
);
265 else if (value
instanceof byte[])
266 JcrUtils
.setBinaryAsBytes(prop
, (byte[]) value
);
267 else if (value
instanceof Instant
) {
268 Instant instant
= (Instant
) value
;
269 GregorianCalendar calendar
= new GregorianCalendar();
270 calendar
.setTime(Date
.from(instant
));
271 prop
.setValue(calendar
);
272 } else // try with toString()
273 prop
.setValue(value
.toString());
274 } catch (RepositoryException e
) {
275 throw new IllegalStateException("Cannot set property " + property
+ " of " + node
+ " to " + value
, e
);
280 * Get property as {@link String}.
282 * @return the value of
283 * {@link Node#getProperty(String)}.{@link Property#getString()} or
284 * <code>null</code> if the property does not exist.
285 * @throws IllegalStateException caused by {@link RepositoryException}
287 public static String
get(Node node
, String property
) {
288 return get(node
, property
, null);
292 * Get property as a {@link String}.
294 * @return the value of
295 * {@link Node#getProperty(String)}.{@link Property#getString()} or
296 * <code>defaultValue</code> if the property does not exist.
297 * @throws IllegalStateException caused by {@link RepositoryException}
299 public static String
get(Node node
, String property
, String defaultValue
) {
301 if (node
.hasProperty(property
))
302 return node
.getProperty(property
).getString();
305 } catch (RepositoryException e
) {
306 throw new IllegalStateException("Cannot retrieve property " + property
+ " from " + node
);
311 * Get property as a {@link Value}.
313 * @return {@link Node#getProperty(String)} or <code>null</code> if the property
315 * @throws IllegalStateException caused by {@link RepositoryException}
317 public static Value
getValue(Node node
, String property
) {
319 if (node
.hasProperty(property
))
320 return node
.getProperty(property
).getValue();
323 } catch (RepositoryException e
) {
324 throw new IllegalStateException("Cannot retrieve property " + property
+ " from " + node
);
329 * Get property doing a best effort to cast it as the target object.
331 * @return the value of {@link Node#getProperty(String)} or
332 * <code>defaultValue</code> if the property does not exist.
333 * @throws IllegalArgumentException if the value could not be cast
334 * @throws IllegalStateException in case of unexpected
335 * {@link RepositoryException}
337 @SuppressWarnings("unchecked")
338 public static <T
> T
getAs(Node node
, String property
, T defaultValue
) {
340 if (node
.hasProperty(property
)) {
341 Property p
= node
.getProperty(property
);
343 switch (p
.getType()) {
344 case PropertyType
.STRING
:
345 return (T
) node
.getProperty(property
).getString();
346 case PropertyType
.DOUBLE
:
347 return (T
) (Double
) node
.getProperty(property
).getDouble();
348 case PropertyType
.LONG
:
349 return (T
) (Long
) node
.getProperty(property
).getLong();
350 case PropertyType
.BOOLEAN
:
351 return (T
) (Boolean
) node
.getProperty(property
).getBoolean();
352 case PropertyType
.DATE
:
353 return (T
) node
.getProperty(property
).getDate();
355 return (T
) node
.getProperty(property
).getString();
357 } catch (ClassCastException e
) {
358 throw new IllegalArgumentException(
359 "Cannot cast property of type " + PropertyType
.nameFromValue(p
.getType()), e
);
364 } catch (RepositoryException e
) {
365 throw new IllegalStateException("Cannot retrieve property " + property
+ " from " + node
);
369 /** Retrieves the {@link Session} related to this node. */
370 public static Session
session(Node node
) {
372 return node
.getSession();
373 } catch (RepositoryException e
) {
374 throw new IllegalStateException("Cannot retrieve session related to " + node
, e
);
379 * Saves the {@link Session} related to this node. Note that all other unrelated
380 * modifications in this session will also be saved.
382 public static void save(Node node
) {
384 Session session
= node
.getSession();
385 // if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
386 // set(node, Property.JCR_LAST_MODIFIED, Instant.now());
387 // set(node, Property.JCR_LAST_MODIFIED_BY, session.getUserID());
389 if (session
.hasPendingChanges())
391 } catch (RepositoryException e
) {
392 throw new IllegalStateException("Cannot save session related to " + node
+ " in workspace "
393 + session(node
).getWorkspace().getName(), e
);
397 /** Login to a JCR repository. */
398 public static Session
login(Repository repository
, String workspace
) {
400 return repository
.login(workspace
);
401 } catch (RepositoryException e
) {
402 throw new IllegalArgumentException("Cannot login to repository", e
);
406 /** Safely and silently logs out a session. */
407 public static void logout(Session session
) {
410 if (session
.isLive())
412 } catch (Exception e
) {
421 * Add a single privilege to a node.
425 public static void addPrivilege(Node node
, String principal
, String privilege
) {
427 Session session
= node
.getSession();
428 JcrUtils
.addPrivilege(session
, node
.getPath(), principal
, privilege
);
429 } catch (RepositoryException e
) {
430 throw new IllegalStateException("Cannot add privilege " + privilege
+ " to " + node
, e
);
437 /** Get checked out status. */
438 public static boolean isCheckedOut(Node node
) {
440 return node
.isCheckedOut();
441 } catch (RepositoryException e
) {
442 throw new IllegalStateException("Cannot retrieve checked out status of " + node
, e
);
446 /** @see VersionManager#checkpoint(String) */
447 public static void checkpoint(Node node
) {
449 versionManager(node
).checkpoint(node
.getPath());
450 } catch (RepositoryException e
) {
451 throw new IllegalStateException("Cannot check in " + node
, e
);
455 /** @see VersionManager#checkin(String) */
456 public static void checkin(Node node
) {
458 versionManager(node
).checkin(node
.getPath());
459 } catch (RepositoryException e
) {
460 throw new IllegalStateException("Cannot check in " + node
, e
);
464 /** @see VersionManager#checkout(String) */
465 public static void checkout(Node node
) {
467 versionManager(node
).checkout(node
.getPath());
468 } catch (RepositoryException e
) {
469 throw new IllegalStateException("Cannot check out " + node
, e
);
473 /** Get the {@link VersionManager} related to this node. */
474 public static VersionManager
versionManager(Node node
) {
476 return node
.getSession().getWorkspace().getVersionManager();
477 } catch (RepositoryException e
) {
478 throw new IllegalStateException("Cannot get version manager from " + node
, e
);
482 /** Get the {@link VersionHistory} related to this node. */
483 public static VersionHistory
getVersionHistory(Node node
) {
485 return versionManager(node
).getVersionHistory(node
.getPath());
486 } catch (RepositoryException e
) {
487 throw new IllegalStateException("Cannot get version history from " + node
, e
);
492 * The linear versions of this version history in reverse order and without the
495 public static List
<Version
> getLinearVersions(VersionHistory versionHistory
) {
497 List
<Version
> lst
= new ArrayList
<>();
498 VersionIterator vit
= versionHistory
.getAllLinearVersions();
499 while (vit
.hasNext())
500 lst
.add(vit
.nextVersion());
502 Collections
.reverse(lst
);
504 } catch (RepositoryException e
) {
505 throw new IllegalStateException("Cannot get linear versions from " + versionHistory
, e
);
509 /** The frozen node related to this {@link Version}. */
510 public static Node
getFrozenNode(Version version
) {
512 return version
.getFrozenNode();
513 } catch (RepositoryException e
) {
514 throw new IllegalStateException("Cannot get frozen node from " + version
, e
);
518 /** Get the base {@link Version} related to this node. */
519 public static Version
getBaseVersion(Node node
) {
521 return versionManager(node
).getBaseVersion(node
.getPath());
522 } catch (RepositoryException e
) {
523 throw new IllegalStateException("Cannot get base version from " + node
, e
);
531 * Returns the size of this file.
533 * @see NodeType#NT_FILE
535 public static long getFileSize(Node fileNode
) {
537 if (!fileNode
.isNodeType(NodeType
.NT_FILE
))
538 throw new IllegalArgumentException(fileNode
+ " must be a file.");
539 return getBinarySize(fileNode
.getNode(Node
.JCR_CONTENT
).getProperty(Property
.JCR_DATA
).getBinary());
540 } catch (RepositoryException e
) {
541 throw new IllegalStateException("Cannot get file size of " + fileNode
, e
);
545 /** Returns the size of this {@link Binary}. */
546 public static long getBinarySize(Binary binaryArg
) {
548 try (Bin binary
= new Bin(binaryArg
)) {
549 return binary
.getSize();
551 } catch (RepositoryException e
) {
552 throw new IllegalStateException("Cannot get file size of binary " + binaryArg
, e
);