]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.jcr/src/org/argeo/jcr/Jcr.java
Clarify naming.
[lgpl/argeo-commons.git] / org.argeo.jcr / src / org / argeo / jcr / Jcr.java
1 package org.argeo.jcr;
2
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;
8 import java.util.Date;
9 import java.util.GregorianCalendar;
10 import java.util.Iterator;
11 import java.util.List;
12
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.nodetype.NodeType;
24 import javax.jcr.security.Privilege;
25 import javax.jcr.version.Version;
26 import javax.jcr.version.VersionHistory;
27 import javax.jcr.version.VersionIterator;
28 import javax.jcr.version.VersionManager;
29
30 /**
31 * Utility class whose purpose is to make using JCR less verbose by
32 * systematically using unchecked exceptions and returning <code>null</code>
33 * when something is not found. This is especially useful when writing user
34 * interfaces (such as with SWT) where listeners and callbacks expect unchecked
35 * exceptions. Loosely inspired by Java's <code>Files</code> singleton.
36 */
37 public class Jcr {
38
39 /**
40 * @see Node#isNodeType(String)
41 * @throws IllegalStateException caused by {@link RepositoryException}
42 */
43 public static boolean isNodeType(Node node, String nodeTypeName) {
44 try {
45 return node.isNodeType(nodeTypeName);
46 } catch (RepositoryException e) {
47 throw new IllegalStateException("Cannot get whether " + node + " is of type " + nodeTypeName, e);
48 }
49 }
50
51 /**
52 * @see Node#hasNodes()
53 * @throws IllegalStateException caused by {@link RepositoryException}
54 */
55 public static boolean hasNodes(Node node) {
56 try {
57 return node.hasNodes();
58 } catch (RepositoryException e) {
59 throw new IllegalStateException("Cannot get whether " + node + " has children.", e);
60 }
61 }
62
63 /**
64 * @see Node#getParent()
65 * @throws IllegalStateException caused by {@link RepositoryException}
66 */
67 public static Node getParent(Node node) {
68 try {
69 return isRoot(node) ? null : node.getParent();
70 } catch (RepositoryException e) {
71 throw new IllegalStateException("Cannot get parent of " + node, e);
72 }
73 }
74
75 /**
76 * Whether this node is the root node.
77 *
78 * @throws IllegalStateException caused by {@link RepositoryException}
79 */
80 public static boolean isRoot(Node node) {
81 try {
82 return node.getDepth() == 0;
83 } catch (RepositoryException e) {
84 throw new IllegalStateException("Cannot get depth of " + node, e);
85 }
86 }
87
88 /**
89 * @see Node#getPath()
90 * @throws IllegalStateException caused by {@link RepositoryException}
91 */
92 public static String getPath(Node node) {
93 try {
94 return node.getPath();
95 } catch (RepositoryException e) {
96 throw new IllegalStateException("Cannot get path of " + node, e);
97 }
98 }
99
100 /**
101 * @see Node#getIdentifier()
102 * @throws IllegalStateException caused by {@link RepositoryException}
103 */
104 public static String getIdentifier(Node node) {
105 try {
106 return node.getIdentifier();
107 } catch (RepositoryException e) {
108 throw new IllegalStateException("Cannot get identifier of " + node, e);
109 }
110 }
111
112 /**
113 * @see Node#getName()
114 * @throws IllegalStateException caused by {@link RepositoryException}
115 */
116 public static String getName(Node node) {
117 try {
118 return node.getName();
119 } catch (RepositoryException e) {
120 throw new IllegalStateException("Cannot get name of " + node, e);
121 }
122 }
123
124 /** Accesses a {@link NodeIterator} as an {@link Iterable}. */
125 @SuppressWarnings("unchecked")
126 public static Iterable<Node> iterate(NodeIterator nodeIterator) {
127 return new Iterable<Node>() {
128
129 @Override
130 public Iterator<Node> iterator() {
131 return nodeIterator;
132 }
133 };
134 }
135
136 /**
137 * @return the children as an {@link Iterable} for use in for-each llops.
138 * @see Node#getNodes()
139 * @throws IllegalStateException caused by {@link RepositoryException}
140 */
141 public static Iterable<Node> nodes(Node node) {
142 try {
143 return iterate(node.getNodes());
144 } catch (RepositoryException e) {
145 throw new IllegalStateException("Cannot get children of " + node, e);
146 }
147 }
148
149 /**
150 * @return the children as a (possibly empty) {@link List}.
151 * @see Node#getNodes()
152 * @throws IllegalStateException caused by {@link RepositoryException}
153 */
154 public static List<Node> getNodes(Node node) {
155 List<Node> nodes = new ArrayList<>();
156 try {
157 if (node.hasNodes()) {
158 NodeIterator nit = node.getNodes();
159 while (nit.hasNext())
160 nodes.add(nit.nextNode());
161 return nodes;
162 } else
163 return nodes;
164 } catch (RepositoryException e) {
165 throw new IllegalStateException("Cannot get children of " + node, e);
166 }
167 }
168
169 /**
170 * @return the child or <code>null</node> if not found
171 * @see Node#getNode(String)
172 * @throws IllegalStateException caused by {@link RepositoryException}
173 */
174 public static Node getNode(Node node, String child) {
175 try {
176 if (node.hasNode(child))
177 return node.getNode(child);
178 else
179 return null;
180 } catch (RepositoryException e) {
181 throw new IllegalStateException("Cannot get child of " + node, e);
182 }
183 }
184
185 /**
186 * @return the node at this path or <code>null</node> if not found
187 * @see Session#getNode(String)
188 * @throws IllegalStateException caused by {@link RepositoryException}
189 */
190 public static Node getNode(Session session, String path) {
191 try {
192 if (session.nodeExists(path))
193 return session.getNode(path);
194 else
195 return null;
196 } catch (RepositoryException e) {
197 throw new IllegalStateException("Cannot get node " + path, e);
198 }
199 }
200
201 /**
202 * @return the node with htis id or <code>null</node> if not found
203 * @see Session#getNodeByIdentifier(String)
204 * @throws IllegalStateException caused by {@link RepositoryException}
205 */
206 public static Node getNodeById(Session session, String id) {
207 try {
208 return session.getNodeByIdentifier(id);
209 } catch (ItemNotFoundException e) {
210 return null;
211 } catch (RepositoryException e) {
212 throw new IllegalStateException("Cannot get node with id " + id, e);
213 }
214 }
215
216 /**
217 * Set a property to the given value, or remove it if the value is
218 * <code>null</code>.
219 *
220 * @throws IllegalStateException caused by {@link RepositoryException}
221 */
222 public static void set(Node node, String property, Object value) {
223 try {
224 if (!node.hasProperty(property))
225 throw new IllegalArgumentException("No property " + property + " in " + node);
226 Property prop = node.getProperty(property);
227 if (value == null) {
228 prop.remove();
229 return;
230 }
231
232 if (value instanceof String)
233 prop.setValue((String) value);
234 else if (value instanceof Long)
235 prop.setValue((Long) value);
236 else if (value instanceof Double)
237 prop.setValue((Double) value);
238 else if (value instanceof Calendar)
239 prop.setValue((Calendar) value);
240 else if (value instanceof BigDecimal)
241 prop.setValue((BigDecimal) value);
242 else if (value instanceof Boolean)
243 prop.setValue((Boolean) value);
244 else if (value instanceof byte[])
245 JcrUtils.setBinaryAsBytes(prop, (byte[]) value);
246 else if (value instanceof Instant) {
247 Instant instant = (Instant) value;
248 GregorianCalendar calendar = new GregorianCalendar();
249 calendar.setTime(Date.from(instant));
250 prop.setValue(calendar);
251 } else // try with toString()
252 prop.setValue(value.toString());
253 } catch (RepositoryException e) {
254 throw new IllegalStateException("Cannot set property " + property + " of " + node + " to " + value, e);
255 }
256 }
257
258 /**
259 * Get property as {@link String}.
260 *
261 * @return the value of
262 * {@link Node#getProperty(String)}.{@link Property#getString()} or
263 * <code>null</code> if the property does not exist.
264 * @throws IllegalStateException caused by {@link RepositoryException}
265 */
266 public static String get(Node node, String property) {
267 return get(node, property, null);
268 }
269
270 /**
271 * Get property as a {@link String}.
272 *
273 * @return the value of
274 * {@link Node#getProperty(String)}.{@link Property#getString()} or
275 * <code>defaultValue</code> if the property does not exist.
276 * @throws IllegalStateException caused by {@link RepositoryException}
277 */
278 public static String get(Node node, String property, String defaultValue) {
279 try {
280 if (node.hasProperty(property))
281 return node.getProperty(property).getString();
282 else
283 return defaultValue;
284 } catch (RepositoryException e) {
285 throw new IllegalStateException("Cannot retrieve property " + property + " from " + node);
286 }
287 }
288
289 /**
290 * Get property as a {@link Value}.
291 *
292 * @return {@link Node#getProperty(String)} or <code>null</code> if the property
293 * does not exist.
294 * @throws IllegalStateException caused by {@link RepositoryException}
295 */
296 public static Value getValue(Node node, String property) {
297 try {
298 if (node.hasProperty(property))
299 return node.getProperty(property).getValue();
300 else
301 return null;
302 } catch (RepositoryException e) {
303 throw new IllegalStateException("Cannot retrieve property " + property + " from " + node);
304 }
305 }
306
307 /**
308 * Get property doing a best effort to cast it as the target object.
309 *
310 * @return the value of {@link Node#getProperty(String)} or
311 * <code>defaultValue</code> if the property does not exist.
312 * @throws IllegalArgumentException if the value could not be cast
313 * @throws IllegalStateException in case of unexpected
314 * {@link RepositoryException}
315 */
316 @SuppressWarnings("unchecked")
317 public static <T> T getAs(Node node, String property, T defaultValue) {
318 try {
319 if (node.hasProperty(property)) {
320 Property p = node.getProperty(property);
321 try {
322 switch (p.getType()) {
323 case PropertyType.STRING:
324 return (T) node.getProperty(property).getString();
325 case PropertyType.DOUBLE:
326 return (T) (Double) node.getProperty(property).getDouble();
327 case PropertyType.LONG:
328 return (T) (Long) node.getProperty(property).getLong();
329 case PropertyType.BOOLEAN:
330 return (T) (Boolean) node.getProperty(property).getBoolean();
331 case PropertyType.DATE:
332 return (T) node.getProperty(property).getDate();
333 default:
334 return (T) node.getProperty(property).getString();
335 }
336 } catch (ClassCastException e) {
337 throw new IllegalArgumentException(
338 "Cannot cast property of type " + PropertyType.nameFromValue(p.getType()), e);
339 }
340 } else {
341 return defaultValue;
342 }
343 } catch (RepositoryException e) {
344 throw new IllegalStateException("Cannot retrieve property " + property + " from " + node);
345 }
346 }
347
348 /** Retrieves the {@link Session} related to this node. */
349 public static Session session(Node node) {
350 try {
351 return node.getSession();
352 } catch (RepositoryException e) {
353 throw new IllegalStateException("Cannot retrieve session related to " + node, e);
354 }
355 }
356
357 /**
358 * Saves the {@link Session} related to this node. Note that all other unrelated
359 * modifications in this session will also be saved.
360 */
361 public static void save(Node node) {
362 try {
363 Session session = node.getSession();
364 // if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
365 // set(node, Property.JCR_LAST_MODIFIED, Instant.now());
366 // set(node, Property.JCR_LAST_MODIFIED_BY, session.getUserID());
367 // }
368 if (session.hasPendingChanges())
369 session.save();
370 } catch (RepositoryException e) {
371 throw new IllegalStateException("Cannot save session related to " + node + " in workspace "
372 + session(node).getWorkspace().getName(), e);
373 }
374 }
375
376 /** Login to a JCR repository. */
377 public static Session login(Repository repository, String workspace) {
378 try {
379 return repository.login(workspace);
380 } catch (RepositoryException e) {
381 throw new IllegalArgumentException("Cannot login to repository", e);
382 }
383 }
384
385 /** Safely and silently logs out a session. */
386 public static void logout(Session session) {
387 try {
388 if (session != null)
389 if (session.isLive())
390 session.logout();
391 } catch (Exception e) {
392 // silent
393 }
394 }
395
396 /*
397 * SECURITY
398 */
399 /**
400 * Add a single privilege to a node.
401 *
402 * @see Privilege
403 */
404 public static void addPrivilege(Node node, String principal, String privilege) {
405 try {
406 Session session = node.getSession();
407 JcrUtils.addPrivilege(session, node.getPath(), principal, privilege);
408 } catch (RepositoryException e) {
409 throw new IllegalStateException("Cannot add privilege " + privilege + " to " + node, e);
410 }
411 }
412
413 /*
414 * VERSIONING
415 */
416 /** Get checked out status. */
417 public static boolean isCheckedOut(Node node) {
418 try {
419 return node.isCheckedOut();
420 } catch (RepositoryException e) {
421 throw new IllegalStateException("Cannot retrieve checked out status of " + node, e);
422 }
423 }
424
425 /** @see VersionManager#checkpoint(String) */
426 public static void checkpoint(Node node) {
427 try {
428 versionManager(node).checkpoint(node.getPath());
429 } catch (RepositoryException e) {
430 throw new IllegalStateException("Cannot check in " + node, e);
431 }
432 }
433
434 /** @see VersionManager#checkin(String) */
435 public static void checkin(Node node) {
436 try {
437 versionManager(node).checkin(node.getPath());
438 } catch (RepositoryException e) {
439 throw new IllegalStateException("Cannot check in " + node, e);
440 }
441 }
442
443 /** @see VersionManager#checkout(String) */
444 public static void checkout(Node node) {
445 try {
446 versionManager(node).checkout(node.getPath());
447 } catch (RepositoryException e) {
448 throw new IllegalStateException("Cannot check out " + node, e);
449 }
450 }
451
452 /** Get the {@link VersionManager} related to this node. */
453 public static VersionManager versionManager(Node node) {
454 try {
455 return node.getSession().getWorkspace().getVersionManager();
456 } catch (RepositoryException e) {
457 throw new IllegalStateException("Cannot get version manager from " + node, e);
458 }
459 }
460
461 /** Get the {@link VersionHistory} related to this node. */
462 public static VersionHistory getVersionHistory(Node node) {
463 try {
464 return versionManager(node).getVersionHistory(node.getPath());
465 } catch (RepositoryException e) {
466 throw new IllegalStateException("Cannot get version history from " + node, e);
467 }
468 }
469
470 /**
471 * The linear versions of this version history in reverse order and without the
472 * root version.
473 */
474 public static List<Version> getLinearVersions(VersionHistory versionHistory) {
475 try {
476 List<Version> lst = new ArrayList<>();
477 VersionIterator vit = versionHistory.getAllLinearVersions();
478 while (vit.hasNext())
479 lst.add(vit.nextVersion());
480 lst.remove(0);
481 Collections.reverse(lst);
482 return lst;
483 } catch (RepositoryException e) {
484 throw new IllegalStateException("Cannot get linear versions from " + versionHistory, e);
485 }
486 }
487
488 /** The frozen node related to this {@link Version}. */
489 public static Node getFrozenNode(Version version) {
490 try {
491 return version.getFrozenNode();
492 } catch (RepositoryException e) {
493 throw new IllegalStateException("Cannot get frozen node from " + version, e);
494 }
495 }
496
497 /** Get the base {@link Version} related to this node. */
498 public static Version getBaseVersion(Node node) {
499 try {
500 return versionManager(node).getBaseVersion(node.getPath());
501 } catch (RepositoryException e) {
502 throw new IllegalStateException("Cannot get base version from " + node, e);
503 }
504 }
505
506 /*
507 * FILES
508 */
509 /**
510 * Returns the size of this file.
511 *
512 * @see NodeType#NT_FILE
513 */
514 public static long getFileSize(Node fileNode) {
515 try {
516 if (!fileNode.isNodeType(NodeType.NT_FILE))
517 throw new IllegalArgumentException(fileNode + " must be a file.");
518 return getBinarySize(fileNode.getNode(Node.JCR_CONTENT).getProperty(Property.JCR_DATA).getBinary());
519 } catch (RepositoryException e) {
520 throw new IllegalStateException("Cannot get file size of " + fileNode, e);
521 }
522 }
523
524 /** Returns the size of this {@link Binary}. */
525 public static long getBinarySize(Binary binaryArg) {
526 try {
527 try (Bin binary = new Bin(binaryArg)) {
528 return binary.getSize();
529 }
530 } catch (RepositoryException e) {
531 throw new IllegalStateException("Cannot get file size of binary " + binaryArg, e);
532 }
533 }
534
535 /** Singleton. */
536 private Jcr() {
537
538 }
539 }