package org.argeo.jcr;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import javax.jcr.Binary;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.Workspace;
import javax.jcr.nodetype.NodeType;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.security.Privilege;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;
import org.apache.commons.io.IOUtils;
/**
* Utility class whose purpose is to make using JCR less verbose by
* systematically using unchecked exceptions and returning null
* when something is not found. This is especially useful when writing user
* interfaces (such as with SWT) where listeners and callbacks expect unchecked
* exceptions. Loosely inspired by Java's Files
singleton.
*/
public class Jcr {
/**
* The name of a node which will be serialized as XML text, as per section 7.3.1
* of the JCR 2.0 specifications.
*/
public final static String JCR_XMLTEXT = "jcr:xmltext";
/**
* The name of a property which will be serialized as XML text, as per section
* 7.3.1 of the JCR 2.0 specifications.
*/
public final static String JCR_XMLCHARACTERS = "jcr:xmlcharacters";
/**
* jcr:name
, when used in another context than
* {@link Property#JCR_NAME}, typically to name a node rather than a property.
*/
public final static String JCR_NAME = "jcr:name";
/**
* jcr:path
, when used in another context than
* {@link Property#JCR_PATH}, typically to name a node rather than a property.
*/
public final static String JCR_PATH = "jcr:path";
/**
* jcr:primaryType
with prefix instead of namespace (as in
* {@link Property#JCR_PRIMARY_TYPE}.
*/
public final static String JCR_PRIMARY_TYPE = "jcr:primaryType";
/**
* jcr:mixinTypes
with prefix instead of namespace (as in
* {@link Property#JCR_MIXIN_TYPES}.
*/
public final static String JCR_MIXIN_TYPES = "jcr:mixinTypes";
/**
* jcr:uuid
with prefix instead of namespace (as in
* {@link Property#JCR_UUID}.
*/
public final static String JCR_UUID = "jcr:uuid";
/**
* jcr:created
with prefix instead of namespace (as in
* {@link Property#JCR_CREATED}.
*/
public final static String JCR_CREATED = "jcr:created";
/**
* jcr:createdBy
with prefix instead of namespace (as in
* {@link Property#JCR_CREATED_BY}.
*/
public final static String JCR_CREATED_BY = "jcr:createdBy";
/**
* jcr:lastModified
with prefix instead of namespace (as in
* {@link Property#JCR_LAST_MODIFIED}.
*/
public final static String JCR_LAST_MODIFIED = "jcr:lastModified";
/**
* jcr:lastModifiedBy
with prefix instead of namespace (as in
* {@link Property#JCR_LAST_MODIFIED_BY}.
*/
public final static String JCR_LAST_MODIFIED_BY = "jcr:lastModifiedBy";
/**
* @see Node#isNodeType(String)
* @throws JcrException caused by {@link RepositoryException}
*/
public static boolean isNodeType(Node node, String nodeTypeName) {
try {
return node.isNodeType(nodeTypeName);
} catch (RepositoryException e) {
throw new JcrException("Cannot get whether " + node + " is of type " + nodeTypeName, e);
}
}
/**
* @see Node#hasNodes()
* @throws JcrException caused by {@link RepositoryException}
*/
public static boolean hasNodes(Node node) {
try {
return node.hasNodes();
} catch (RepositoryException e) {
throw new JcrException("Cannot get whether " + node + " has children.", e);
}
}
/**
* @see Node#getParent()
* @throws JcrException caused by {@link RepositoryException}
*/
public static Node getParent(Node node) {
try {
return isRoot(node) ? null : node.getParent();
} catch (RepositoryException e) {
throw new JcrException("Cannot get parent of " + node, e);
}
}
/**
* @see Node#getParent()
* @throws JcrException caused by {@link RepositoryException}
*/
public static String getParentPath(Node node) {
return getPath(getParent(node));
}
/**
* Whether this node is the root node.
*
* @throws JcrException caused by {@link RepositoryException}
*/
public static boolean isRoot(Node node) {
try {
return node.getDepth() == 0;
} catch (RepositoryException e) {
throw new JcrException("Cannot get depth of " + node, e);
}
}
/**
* @see Node#getPath()
* @throws JcrException caused by {@link RepositoryException}
*/
public static String getPath(Node node) {
try {
return node.getPath();
} catch (RepositoryException e) {
throw new JcrException("Cannot get path of " + node, e);
}
}
/**
* @see Node#getSession()
* @see Session#getWorkspace()
* @see Workspace#getName()
*/
public static String getWorkspaceName(Node node) {
return session(node).getWorkspace().getName();
}
/**
* @see Node#getIdentifier()
* @throws JcrException caused by {@link RepositoryException}
*/
public static String getIdentifier(Node node) {
try {
return node.getIdentifier();
} catch (RepositoryException e) {
throw new JcrException("Cannot get identifier of " + node, e);
}
}
/**
* @see Node#getName()
* @throws JcrException caused by {@link RepositoryException}
*/
public static String getName(Node node) {
try {
return node.getName();
} catch (RepositoryException e) {
throw new JcrException("Cannot get name of " + node, e);
}
}
/**
* Returns the node name with its current index (useful for re-ordering).
*
* @see Node#getName()
* @see Node#getIndex()
* @throws JcrException caused by {@link RepositoryException}
*/
public static String getIndexedName(Node node) {
try {
return node.getName() + "[" + node.getIndex() + "]";
} catch (RepositoryException e) {
throw new JcrException("Cannot get name of " + node, e);
}
}
/**
* @see Node#getProperty(String)
* @throws JcrException caused by {@link RepositoryException}
*/
public static Property getProperty(Node node, String property) {
try {
if (node.hasProperty(property))
return node.getProperty(property);
else
return null;
} catch (RepositoryException e) {
throw new JcrException("Cannot get property " + property + " of " + node, e);
}
}
/**
* @see Node#getIndex()
* @throws JcrException caused by {@link RepositoryException}
*/
public static int getIndex(Node node) {
try {
return node.getIndex();
} catch (RepositoryException e) {
throw new JcrException("Cannot get index of " + node, e);
}
}
/**
* If node has mixin {@link NodeType#MIX_TITLE}, return
* {@link Property#JCR_TITLE}, otherwise return {@link #getName(Node)}.
*/
public static String getTitle(Node node) {
if (Jcr.isNodeType(node, NodeType.MIX_TITLE))
return get(node, Property.JCR_TITLE);
else
return Jcr.getName(node);
}
/** Accesses a {@link NodeIterator} as an {@link Iterable}. */
@SuppressWarnings("unchecked")
public static Iterable iterate(NodeIterator nodeIterator) {
return new Iterable() {
@Override
public Iterator iterator() {
return nodeIterator;
}
};
}
/**
* @return the children as an {@link Iterable} for use in for-each llops.
* @see Node#getNodes()
* @throws JcrException caused by {@link RepositoryException}
*/
public static Iterable nodes(Node node) {
try {
return iterate(node.getNodes());
} catch (RepositoryException e) {
throw new JcrException("Cannot get children of " + node, e);
}
}
/**
* @return the children as a (possibly empty) {@link List}.
* @see Node#getNodes()
* @throws JcrException caused by {@link RepositoryException}
*/
public static List getNodes(Node node) {
List nodes = new ArrayList<>();
try {
if (node.hasNodes()) {
NodeIterator nit = node.getNodes();
while (nit.hasNext())
nodes.add(nit.nextNode());
return nodes;
} else
return nodes;
} catch (RepositoryException e) {
throw new JcrException("Cannot get children of " + node, e);
}
}
/**
* @return the child or null
if not found
* @see Node#getNode(String)
* @throws JcrException caused by {@link RepositoryException}
*/
public static Node getNode(Node node, String child) {
try {
if (node.hasNode(child))
return node.getNode(child);
else
return null;
} catch (RepositoryException e) {
throw new JcrException("Cannot get child of " + node, e);
}
}
/**
* @return the node at this path or null
if not found
* @see Session#getNode(String)
* @throws JcrException caused by {@link RepositoryException}
*/
public static Node getNode(Session session, String path) {
try {
if (session.nodeExists(path))
return session.getNode(path);
else
return null;
} catch (RepositoryException e) {
throw new JcrException("Cannot get node " + path, e);
}
}
/**
* Add a node to this parent, setting its primary type and its mixins.
*
* @param parent the parent node
* @param name the name of the node, if null
, the primary
* type will be used (typically for XML structures)
* @param primaryType the primary type, if null
* {@link NodeType#NT_UNSTRUCTURED} will be used.
* @param mixins the mixins
* @return the created node
* @see Node#addNode(String, String)
* @see Node#addMixin(String)
*/
public static Node addNode(Node parent, String name, String primaryType, String... mixins) {
if (name == null && primaryType == null)
throw new IllegalArgumentException("Both node name and primary type cannot be null");
try {
Node newNode = parent.addNode(name == null ? primaryType : name,
primaryType == null ? NodeType.NT_UNSTRUCTURED : primaryType);
for (String mixin : mixins) {
newNode.addMixin(mixin);
}
return newNode;
} catch (RepositoryException e) {
throw new JcrException("Cannot add node " + name + " to " + parent, e);
}
}
/**
* Add an {@link NodeType#NT_BASE} node to this parent.
*
* @param parent the parent node
* @param name the name of the node, cannot be null
* @return the created node
*
* @see Node#addNode(String)
*/
public static Node addNode(Node parent, String name) {
if (name == null)
throw new IllegalArgumentException("Node name cannot be null");
try {
Node newNode = parent.addNode(name);
return newNode;
} catch (RepositoryException e) {
throw new JcrException("Cannot add node " + name + " to " + parent, e);
}
}
/**
* Add mixins to a node.
*
* @param node the node
* @param mixins the mixins
* @return the created node
* @see Node#addMixin(String)
*/
public static void addMixin(Node node, String... mixins) {
try {
for (String mixin : mixins) {
node.addMixin(mixin);
}
} catch (RepositoryException e) {
throw new JcrException("Cannot add mixins " + Arrays.asList(mixins) + " to " + node, e);
}
}
/**
* Removes this node.
*
* @see Node#remove()
*/
public static void remove(Node node) {
try {
node.remove();
} catch (RepositoryException e) {
throw new JcrException("Cannot remove node " + node, e);
}
}
/**
* @return the node with htis id or null
if not found
* @see Session#getNodeByIdentifier(String)
* @throws JcrException caused by {@link RepositoryException}
*/
public static Node getNodeById(Session session, String id) {
try {
return session.getNodeByIdentifier(id);
} catch (ItemNotFoundException e) {
return null;
} catch (RepositoryException e) {
throw new JcrException("Cannot get node with id " + id, e);
}
}
/**
* Set a property to the given value, or remove it if the value is
* null
.
*
* @throws JcrException caused by {@link RepositoryException}
*/
public static void set(Node node, String property, Object value) {
try {
if (!node.hasProperty(property)) {
if (value != null) {
if (value instanceof List) {// multiple
List> lst = (List>) value;
String[] values = new String[lst.size()];
for (int i = 0; i < lst.size(); i++) {
values[i] = lst.get(i).toString();
}
node.setProperty(property, values);
} else {
node.setProperty(property, value.toString());
}
}
return;
}
Property prop = node.getProperty(property);
if (value == null) {
prop.remove();
return;
}
// multiple
if (value instanceof List) {
List> lst = (List>) value;
String[] values = new String[lst.size()];
// TODO better cast?
for (int i = 0; i < lst.size(); i++) {
values[i] = lst.get(i).toString();
}
if (!prop.isMultiple())
prop.remove();
node.setProperty(property, values);
return;
}
// single
if (prop.isMultiple()) {
prop.remove();
node.setProperty(property, value.toString());
return;
}
if (value instanceof String)
prop.setValue((String) value);
else if (value instanceof Long)
prop.setValue((Long) value);
else if (value instanceof Integer)
prop.setValue(((Integer) value).longValue());
else if (value instanceof Double)
prop.setValue((Double) value);
else if (value instanceof Float)
prop.setValue(((Float) value).doubleValue());
else if (value instanceof Calendar)
prop.setValue((Calendar) value);
else if (value instanceof BigDecimal)
prop.setValue((BigDecimal) value);
else if (value instanceof Boolean)
prop.setValue((Boolean) value);
else if (value instanceof byte[])
JcrUtils.setBinaryAsBytes(prop, (byte[]) value);
else if (value instanceof Instant) {
Instant instant = (Instant) value;
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(Date.from(instant));
prop.setValue(calendar);
} else // try with toString()
prop.setValue(value.toString());
} catch (RepositoryException e) {
throw new JcrException("Cannot set property " + property + " of " + node + " to " + value, e);
}
}
/**
* Get property as {@link String}.
*
* @return the value of
* {@link Node#getProperty(String)}.{@link Property#getString()} or
* null
if the property does not exist.
* @throws JcrException caused by {@link RepositoryException}
*/
public static String get(Node node, String property) {
return get(node, property, null);
}
/**
* Get property as a {@link String}. If the property is multiple it returns the
* first value.
*
* @return the value of
* {@link Node#getProperty(String)}.{@link Property#getString()} or
* defaultValue
if the property does not exist.
* @throws JcrException caused by {@link RepositoryException}
*/
public static String get(Node node, String property, String defaultValue) {
try {
if (node.hasProperty(property)) {
Property p = node.getProperty(property);
if (!p.isMultiple())
return p.getString();
else {
Value[] values = p.getValues();
if (values.length == 0)
return defaultValue;
else
return values[0].getString();
}
} else
return defaultValue;
} catch (RepositoryException e) {
throw new JcrException("Cannot retrieve property " + property + " from " + node, e);
}
}
/**
* Get property as a {@link Value}.
*
* @return {@link Node#getProperty(String)} or null
if the property
* does not exist.
* @throws JcrException caused by {@link RepositoryException}
*/
public static Value getValue(Node node, String property) {
try {
if (node.hasProperty(property))
return node.getProperty(property).getValue();
else
return null;
} catch (RepositoryException e) {
throw new JcrException("Cannot retrieve property " + property + " from " + node, e);
}
}
/**
* Get property doing a best effort to cast it as the target object.
*
* @return the value of {@link Node#getProperty(String)} or
* defaultValue
if the property does not exist.
* @throws IllegalArgumentException if the value could not be cast
* @throws JcrException in case of unexpected
* {@link RepositoryException}
*/
@SuppressWarnings("unchecked")
public static T getAs(Node node, String property, T defaultValue) {
try {
// TODO deal with multiple
if (node.hasProperty(property)) {
Property p = node.getProperty(property);
try {
if (p.isMultiple()) {
throw new UnsupportedOperationException("Multiple values properties are not supported");
}
Value value = p.getValue();
return (T) get(value);
} catch (ClassCastException e) {
throw new IllegalArgumentException(
"Cannot cast property of type " + PropertyType.nameFromValue(p.getType()), e);
}
} else {
return defaultValue;
}
} catch (RepositoryException e) {
throw new JcrException("Cannot retrieve property " + property + " from " + node, e);
}
}
public static T getAs(Node node, String property, Class clss) {
if(String.class.isAssignableFrom(clss)) {
return (T)get(node,property);
} else if(Long.class.isAssignableFrom(clss)) {
return (T)get(node,property);
}else {
throw new IllegalArgumentException("Unsupported format "+clss);
}
}
/**
* Get a multiple property as a list, doing a best effort to cast it as the
* target list.
*
* @return the value of {@link Node#getProperty(String)}.
* @throws IllegalArgumentException if the value could not be cast
* @throws JcrException in case of unexpected
* {@link RepositoryException}
*/
public static List getMultiple(Node node, String property) {
try {
if (node.hasProperty(property)) {
Property p = node.getProperty(property);
return getMultiple(p);
} else {
return null;
}
} catch (RepositoryException e) {
throw new JcrException("Cannot retrieve multiple values property " + property + " from " + node, e);
}
}
/**
* Get a multiple property as a list, doing a best effort to cast it as the
* target list.
*/
@SuppressWarnings("unchecked")
public static List getMultiple(Property p) {
try {
List res = new ArrayList<>();
if (!p.isMultiple()) {
res.add((T) get(p.getValue()));
return res;
}
Value[] values = p.getValues();
for (Value value : values) {
res.add((T) get(value));
}
return res;
} catch (ClassCastException | RepositoryException e) {
throw new IllegalArgumentException("Cannot get property " + p, e);
}
}
/** Cast a {@link Value} to a standard Java object. */
public static Object get(Value value) {
Binary binary = null;
try {
switch (value.getType()) {
case PropertyType.STRING:
return value.getString();
case PropertyType.DOUBLE:
return (Double) value.getDouble();
case PropertyType.LONG:
return (Long) value.getLong();
case PropertyType.BOOLEAN:
return (Boolean) value.getBoolean();
case PropertyType.DATE:
return value.getDate();
case PropertyType.BINARY:
binary = value.getBinary();
byte[] arr = null;
try (InputStream in = binary.getStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
IOUtils.copy(in, out);
arr = out.toByteArray();
} catch (IOException e) {
throw new RuntimeException("Cannot read binary from " + value, e);
}
return arr;
default:
return value.getString();
}
} catch (RepositoryException e) {
throw new JcrException("Cannot cast value from " + value, e);
} finally {
if (binary != null)
binary.dispose();
}
}
/**
* Retrieves the {@link Session} related to this node.
*
* @deprecated Use {@link #getSession(Node)} instead.
*/
@Deprecated
public static Session session(Node node) {
return getSession(node);
}
/** Retrieves the {@link Session} related to this node. */
public static Session getSession(Node node) {
try {
return node.getSession();
} catch (RepositoryException e) {
throw new JcrException("Cannot retrieve session related to " + node, e);
}
}
/** Retrieves the root node related to this session. */
public static Node getRootNode(Session session) {
try {
return session.getRootNode();
} catch (RepositoryException e) {
throw new JcrException("Cannot get root node for " + session, e);
}
}
/** Whether this item exists. */
public static boolean itemExists(Session session, String path) {
try {
return session.itemExists(path);
} catch (RepositoryException e) {
throw new JcrException("Cannot check whether " + path + " exists", e);
}
}
/**
* Saves the {@link Session} related to this node. Note that all other unrelated
* modifications in this session will also be saved.
*/
public static void save(Node node) {
try {
Session session = node.getSession();
// if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
// set(node, Property.JCR_LAST_MODIFIED, Instant.now());
// set(node, Property.JCR_LAST_MODIFIED_BY, session.getUserID());
// }
if (session.hasPendingChanges())
session.save();
} catch (RepositoryException e) {
throw new JcrException("Cannot save session related to " + node + " in workspace "
+ session(node).getWorkspace().getName(), e);
}
}
/** Login to a JCR repository. */
public static Session login(Repository repository, String workspace) {
try {
return repository.login(workspace);
} catch (RepositoryException e) {
throw new IllegalArgumentException("Cannot login to repository", e);
}
}
/** Safely and silently logs out a session. */
public static void logout(Session session) {
try {
if (session != null)
if (session.isLive())
session.logout();
} catch (Exception e) {
// silent
}
}
/** Safely and silently logs out the underlying session. */
public static void logout(Node node) {
Jcr.logout(session(node));
}
/*
* SECURITY
*/
/**
* Add a single privilege to a node.
*
* @see Privilege
*/
public static void addPrivilege(Node node, String principal, String privilege) {
try {
Session session = node.getSession();
JcrUtils.addPrivilege(session, node.getPath(), principal, privilege);
} catch (RepositoryException e) {
throw new JcrException("Cannot add privilege " + privilege + " to " + node, e);
}
}
/*
* VERSIONING
*/
/** Get checked out status. */
public static boolean isCheckedOut(Node node) {
try {
return node.isCheckedOut();
} catch (RepositoryException e) {
throw new JcrException("Cannot retrieve checked out status of " + node, e);
}
}
/** @see VersionManager#checkpoint(String) */
public static void checkpoint(Node node) {
try {
versionManager(node).checkpoint(node.getPath());
} catch (RepositoryException e) {
throw new JcrException("Cannot check in " + node, e);
}
}
/** @see VersionManager#checkin(String) */
public static void checkin(Node node) {
try {
versionManager(node).checkin(node.getPath());
} catch (RepositoryException e) {
throw new JcrException("Cannot check in " + node, e);
}
}
/** @see VersionManager#checkout(String) */
public static void checkout(Node node) {
try {
versionManager(node).checkout(node.getPath());
} catch (RepositoryException e) {
throw new JcrException("Cannot check out " + node, e);
}
}
/** Get the {@link VersionManager} related to this node. */
public static VersionManager versionManager(Node node) {
try {
return node.getSession().getWorkspace().getVersionManager();
} catch (RepositoryException e) {
throw new JcrException("Cannot get version manager from " + node, e);
}
}
/** Get the {@link VersionHistory} related to this node. */
public static VersionHistory getVersionHistory(Node node) {
try {
return versionManager(node).getVersionHistory(node.getPath());
} catch (RepositoryException e) {
throw new JcrException("Cannot get version history from " + node, e);
}
}
/**
* The linear versions of this version history in reverse order and without the
* root version.
*/
public static List getLinearVersions(VersionHistory versionHistory) {
try {
List lst = new ArrayList<>();
VersionIterator vit = versionHistory.getAllLinearVersions();
while (vit.hasNext())
lst.add(vit.nextVersion());
lst.remove(0);
Collections.reverse(lst);
return lst;
} catch (RepositoryException e) {
throw new JcrException("Cannot get linear versions from " + versionHistory, e);
}
}
/** The frozen node related to this {@link Version}. */
public static Node getFrozenNode(Version version) {
try {
return version.getFrozenNode();
} catch (RepositoryException e) {
throw new JcrException("Cannot get frozen node from " + version, e);
}
}
/** Get the base {@link Version} related to this node. */
public static Version getBaseVersion(Node node) {
try {
return versionManager(node).getBaseVersion(node.getPath());
} catch (RepositoryException e) {
throw new JcrException("Cannot get base version from " + node, e);
}
}
/*
* FILES
*/
/**
* Returns the size of this file.
*
* @see NodeType#NT_FILE
*/
public static long getFileSize(Node fileNode) {
try {
if (!fileNode.isNodeType(NodeType.NT_FILE))
throw new IllegalArgumentException(fileNode + " must be a file.");
return getBinarySize(fileNode.getNode(Node.JCR_CONTENT).getProperty(Property.JCR_DATA).getBinary());
} catch (RepositoryException e) {
throw new JcrException("Cannot get file size of " + fileNode, e);
}
}
/** Returns the size of this {@link Binary}. */
public static long getBinarySize(Binary binaryArg) {
try {
try (Bin binary = new Bin(binaryArg)) {
return binary.getSize();
}
} catch (RepositoryException e) {
throw new JcrException("Cannot get file size of binary " + binaryArg, e);
}
}
// QUERY
/** Creates a JCR-SQL2 query using {@link MessageFormat}. */
public static Query createQuery(QueryManager qm, String sql, Object... args) {
// fix single quotes
sql = sql.replaceAll("'", "''");
String query = MessageFormat.format(sql, args);
try {
return qm.createQuery(query, Query.JCR_SQL2);
} catch (RepositoryException e) {
throw new JcrException("Cannot create JCR-SQL2 query from " + query, e);
}
}
/** Executes a JCR-SQL2 query using {@link MessageFormat}. */
public static NodeIterator executeQuery(QueryManager qm, String sql, Object... args) {
Query query = createQuery(qm, sql, args);
try {
return query.execute().getNodes();
} catch (RepositoryException e) {
throw new JcrException("Cannot execute query " + sql + " with arguments " + Arrays.asList(args), e);
}
}
/** Executes a JCR-SQL2 query using {@link MessageFormat}. */
public static NodeIterator executeQuery(Session session, String sql, Object... args) {
QueryManager queryManager;
try {
queryManager = session.getWorkspace().getQueryManager();
} catch (RepositoryException e) {
throw new JcrException("Cannot get query manager from session " + session, e);
}
return executeQuery(queryManager, sql, args);
}
/**
* Executes a JCR-SQL2 query using {@link MessageFormat}, which must return a
* single node at most.
*
* @return the node or null
if not found.
*/
public static Node getNode(QueryManager qm, String sql, Object... args) {
NodeIterator nit = executeQuery(qm, sql, args);
if (nit.hasNext()) {
Node node = nit.nextNode();
if (nit.hasNext())
throw new IllegalStateException(
"Query " + sql + " with arguments " + Arrays.asList(args) + " returned more than one node.");
return node;
} else {
return null;
}
}
/**
* Executes a JCR-SQL2 query using {@link MessageFormat}, which must return a
* single node at most.
*
* @return the node or null
if not found.
*/
public static Node getNode(Session session, String sql, Object... args) {
QueryManager queryManager;
try {
queryManager = session.getWorkspace().getQueryManager();
} catch (RepositoryException e) {
throw new JcrException("Cannot get query manager from session " + session, e);
}
return getNode(queryManager, sql, args);
}
/** Singleton. */
private Jcr() {
}
}