Introduce Jcr singleton, making using Jcr less verbose.
[lgpl/argeo-commons.git] / org.argeo.jcr / src / org / argeo / jcr / JcrUtils.java
index 8ae143d7ac4aefd2b0db4e2fc4f9fbbf22279836..331164cf2b442dec485bb7a71ebb0d7a0c50dd65 100644 (file)
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.security.MessageDigest;
 import java.security.Principal;
 import java.text.DateFormat;
 import java.text.ParseException;
@@ -63,7 +64,6 @@ import javax.jcr.security.Privilege;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.argeo.util.DigestUtils;
 
 /** Utility methods to simplify common JCR operations. */
 public class JcrUtils {
@@ -75,10 +75,8 @@ public class JcrUtils {
         * http://www.day.com/specs/jcr/2.0/3_Repository_Model.html#3.2.2%20Local
         * %20Names
         */
-       public final static char[] INVALID_NAME_CHARACTERS = { '/', ':', '[', ']', '|', '*', /*
-                                                                                                                                                                                        * invalid XML chars :
-                                                                                                                                                                                        */
-                       '<', '>', '&' };
+       public final static char[] INVALID_NAME_CHARACTERS = { '/', ':', '[', ']', '|', '*', /* invalid for XML: */ '<',
+                       '>', '&' };
 
        /** Prevents instantiation */
        private JcrUtils() {
@@ -88,8 +86,7 @@ public class JcrUtils {
         * Queries one single node.
         * 
         * @return one single node or null if none was found
-        * @throws ArgeoJcrException
-        *             if more than one node was found
+        * @throws ArgeoJcrException if more than one node was found
         */
        public static Node querySingleNode(Query query) {
                NodeIterator nodeIterator;
@@ -238,10 +235,8 @@ public class JcrUtils {
        /**
         * The provided data as a path ('/' at the end, not the beginning)
         * 
-        * @param cal
-        *            the date
-        * @param addHour
-        *            whether to add hour as well
+        * @param cal     the date
+        * @param addHour whether to add hour as well
         */
        public static String dateAsPath(Calendar cal, Boolean addHour) {
                StringBuffer buf = new StringBuffer(14);
@@ -365,6 +360,15 @@ public class JcrUtils {
                }
        }
 
+       /** Concisely get the path of the given node. */
+       public static String getPath(Node node) {
+               try {
+                       return node.getPath();
+               } catch (RepositoryException e) {
+                       throw new ArgeoJcrException("Cannot get path of " + node, e);
+               }
+       }
+
        /** Concisely get the boolean value of a property */
        public static Boolean check(Node node, String propertyName) {
                try {
@@ -397,8 +401,7 @@ public class JcrUtils {
        /**
         * Create sub nodes relative to a parent node
         * 
-        * @param nodeType
-        *            the type of the leaf node
+        * @param nodeType the type of the leaf node
         */
        public static Node mkdirs(Node parentNode, String relativePath, String nodeType) {
                return mkdirs(parentNode, relativePath, nodeType, null);
@@ -407,8 +410,7 @@ public class JcrUtils {
        /**
         * Create sub nodes relative to a parent node
         * 
-        * @param nodeType
-        *            the type of the leaf node
+        * @param nodeType the type of the leaf node
         */
        public static Node mkdirs(Node parentNode, String relativePath, String nodeType, String intermediaryNodeType) {
                List<String> tokens = tokenize(relativePath);
@@ -459,8 +461,7 @@ public class JcrUtils {
        }
 
        /**
-        * @param type
-        *            the type of the leaf node
+        * @param type the type of the leaf node
         */
        public static Node mkdirs(Session session, String path, String type) {
                return mkdirs(session, path, type, null, false);
@@ -474,7 +475,7 @@ public class JcrUtils {
        public static Node mkdirs(Session session, String path, String type, String intermediaryNodeType,
                        Boolean versioning) {
                try {
-                       if (path.equals('/'))
+                       if (path.equals("/"))
                                return session.getRootNode();
 
                        if (session.itemExists(path)) {
@@ -949,15 +950,16 @@ public class JcrUtils {
                        return name;
        }
 
-       /**
-        * Removes forbidden characters from a path, replacing them with '_'
-        * 
-        * @deprecated use {@link #replaceInvalidChars(String)} instead
-        */
-       public static String removeForbiddenCharacters(String str) {
-               return str.replace('[', '_').replace(']', '_').replace('/', '_').replace('*', '_');
-
-       }
+       // /**
+       // * Removes forbidden characters from a path, replacing them with '_'
+       // *
+       // * @deprecated use {@link #replaceInvalidChars(String)} instead
+       // */
+       // public static String removeForbiddenCharacters(String str) {
+       // return str.replace('[', '_').replace(']', '_').replace('/', '_').replace('*',
+       // '_');
+       //
+       // }
 
        /** Cleanly disposes a {@link Binary} even if it is null. */
        public static void closeQuietly(Binary binary) {
@@ -968,35 +970,37 @@ public class JcrUtils {
 
        /** Retrieve a {@link Binary} as a byte array */
        public static byte[] getBinaryAsBytes(Property property) {
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               InputStream in = null;
-               Binary binary = null;
-               try {
-                       binary = property.getBinary();
-                       in = binary.getStream();
+               // ByteArrayOutputStream out = new ByteArrayOutputStream();
+               // InputStream in = null;
+               // Binary binary = null;
+               try (ByteArrayOutputStream out = new ByteArrayOutputStream();
+                               Bin binary = new Bin(property);
+                               InputStream in = binary.getStream()) {
+                       // binary = property.getBinary();
+                       // in = binary.getStream();
                        IOUtils.copy(in, out);
                        return out.toByteArray();
                } catch (Exception e) {
                        throw new ArgeoJcrException("Cannot read binary " + property + " as bytes", e);
                } finally {
-                       IOUtils.closeQuietly(out);
-                       IOUtils.closeQuietly(in);
-                       closeQuietly(binary);
+                       // IOUtils.closeQuietly(out);
+                       // IOUtils.closeQuietly(in);
+                       // closeQuietly(binary);
                }
        }
 
        /** Writes a {@link Binary} from a byte array */
        public static void setBinaryAsBytes(Node node, String property, byte[] bytes) {
-               InputStream in = null;
+               // InputStream in = null;
                Binary binary = null;
-               try {
-                       in = new ByteArrayInputStream(bytes);
+               try (InputStream in = new ByteArrayInputStream(bytes)) {
+                       // in = new ByteArrayInputStream(bytes);
                        binary = node.getSession().getValueFactory().createBinary(in);
                        node.setProperty(property, binary);
                } catch (Exception e) {
                        throw new ArgeoJcrException("Cannot read binary " + property + " as bytes", e);
                } finally {
-                       IOUtils.closeQuietly(in);
+                       // IOUtils.closeQuietly(in);
                        closeQuietly(binary);
                }
        }
@@ -1070,15 +1074,19 @@ public class JcrUtils {
                }
        }
 
-       /** Logs out the session, not throwing any exception, even if it is null. */
+       /**
+        * Logs out the session, not throwing any exception, even if it is null.
+        * {@link Jcr#logout(Session)} should rather be used.
+        */
        public static void logoutQuietly(Session session) {
-               try {
-                       if (session != null)
-                               if (session.isLive())
-                                       session.logout();
-               } catch (Exception e) {
-                       // silent
-               }
+               Jcr.logout(session);
+//             try {
+//                     if (session != null)
+//                             if (session.isLive())
+//                                     session.logout();
+//             } catch (Exception e) {
+//                     // silent
+//             }
        }
 
        /**
@@ -1156,10 +1164,8 @@ public class JcrUtils {
        /**
         * Update lastModified recursively until this parent.
         * 
-        * @param node
-        *            the node
-        * @param untilPath
-        *            the base path, null is equivalent to "/"
+        * @param node      the node
+        * @param untilPath the base path, null is equivalent to "/"
         */
        public static void updateLastModifiedAndParents(Node node, String untilPath) {
                try {
@@ -1346,15 +1352,15 @@ public class JcrUtils {
         * Copy only nt:folder and nt:file, without their additional types and
         * properties.
         * 
-        * @param recursive
-        *            if true copies folders as well, otherwise only first level files
+        * @param recursive if true copies folders as well, otherwise only first level
+        *                  files
         * @return how many files were copied
         */
        public static Long copyFiles(Node fromNode, Node toNode, Boolean recursive, JcrMonitor monitor, boolean onlyAdd) {
                long count = 0l;
 
-               Binary binary = null;
-               InputStream in = null;
+               // Binary binary = null;
+               // InputStream in = null;
                try {
                        NodeIterator fromChildren = fromNode.getNodes();
                        children: while (fromChildren.hasNext()) {
@@ -1371,11 +1377,12 @@ public class JcrUtils {
 
                                        if (monitor != null)
                                                monitor.subTask("Copy " + fileName);
-                                       binary = fromChild.getNode(Node.JCR_CONTENT).getProperty(Property.JCR_DATA).getBinary();
-                                       in = binary.getStream();
-                                       copyStreamAsFile(toNode, fileName, in);
-                                       IOUtils.closeQuietly(in);
-                                       closeQuietly(binary);
+                                       try (Bin binary = new Bin(fromChild.getNode(Node.JCR_CONTENT).getProperty(Property.JCR_DATA));
+                                                       InputStream in = binary.getStream();) {
+                                               copyStreamAsFile(toNode, fileName, in);
+                                       }
+                                       // IOUtils.closeQuietly(in);
+                                       // closeQuietly(binary);
 
                                        // save session
                                        toNode.getSession().save();
@@ -1401,12 +1408,12 @@ public class JcrUtils {
                                }
                        }
                        return count;
-               } catch (RepositoryException e) {
+               } catch (RepositoryException | IOException e) {
                        throw new ArgeoJcrException("Cannot copy files between " + fromNode + " and " + toNode);
                } finally {
                        // in case there was an exception
-                       IOUtils.closeQuietly(in);
-                       closeQuietly(binary);
+                       // IOUtils.closeQuietly(in);
+                       // closeQuietly(binary);
                }
        }
 
@@ -1437,27 +1444,27 @@ public class JcrUtils {
         * @return the created file node
         */
        public static Node copyFile(Node folderNode, File file) {
-               InputStream in = null;
-               try {
-                       in = new FileInputStream(file);
+               // InputStream in = null;
+               try (InputStream in = new FileInputStream(file)) {
+                       // in = new FileInputStream(file);
                        return copyStreamAsFile(folderNode, file.getName(), in);
                } catch (IOException e) {
                        throw new ArgeoJcrException("Cannot copy file " + file + " under " + folderNode, e);
-               } finally {
-                       IOUtils.closeQuietly(in);
+                       // } finally {
+                       // IOUtils.closeQuietly(in);
                }
        }
 
        /** Copy bytes as an nt:file */
        public static Node copyBytesAsFile(Node folderNode, String fileName, byte[] bytes) {
-               InputStream in = null;
-               try {
-                       in = new ByteArrayInputStream(bytes);
+               // InputStream in = null;
+               try (InputStream in = new ByteArrayInputStream(bytes)) {
+                       // in = new ByteArrayInputStream(bytes);
                        return copyStreamAsFile(folderNode, fileName, in);
                } catch (Exception e) {
                        throw new ArgeoJcrException("Cannot copy file " + fileName + " under " + folderNode, e);
-               } finally {
-                       IOUtils.closeQuietly(in);
+                       // } finally {
+                       // IOUtils.closeQuietly(in);
                }
        }
 
@@ -1492,20 +1499,58 @@ public class JcrUtils {
                }
        }
 
-       /** Computes the checksum of an nt:file */
+       /**
+        * Computes the checksum of an nt:file.
+        * 
+        * @deprecated use separate digest utilities
+        */
+       @Deprecated
        public static String checksumFile(Node fileNode, String algorithm) {
                Binary data = null;
-               InputStream in = null;
-               try {
-                       data = fileNode.getNode(Node.JCR_CONTENT).getProperty(Property.JCR_DATA).getBinary();
-                       in = data.getStream();
-                       return DigestUtils.digest(algorithm, in);
-               } catch (RepositoryException e) {
+               try (InputStream in = fileNode.getNode(Node.JCR_CONTENT).getProperty(Property.JCR_DATA).getBinary()
+                               .getStream()) {
+                       return digest(algorithm, in);
+               } catch (RepositoryException | IOException e) {
                        throw new ArgeoJcrException("Cannot checksum file " + fileNode, e);
                } finally {
-                       IOUtils.closeQuietly(in);
                        closeQuietly(data);
                }
        }
 
+       @Deprecated
+       private static String digest(String algorithm, InputStream in) {
+               final Integer byteBufferCapacity = 100 * 1024;// 100 KB
+               try {
+                       MessageDigest digest = MessageDigest.getInstance(algorithm);
+                       byte[] buffer = new byte[byteBufferCapacity];
+                       int read = 0;
+                       while ((read = in.read(buffer)) > 0) {
+                               digest.update(buffer, 0, read);
+                       }
+
+                       byte[] checksum = digest.digest();
+                       String res = encodeHexString(checksum);
+                       return res;
+               } catch (Exception e) {
+                       throw new ArgeoJcrException("Cannot digest with algorithm " + algorithm, e);
+               }
+       }
+
+       /**
+        * From
+        * http://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to
+        * -a-hex-string-in-java
+        */
+       @Deprecated
+       private static String encodeHexString(byte[] bytes) {
+               final char[] hexArray = "0123456789abcdef".toCharArray();
+               char[] hexChars = new char[bytes.length * 2];
+               for (int j = 0; j < bytes.length; j++) {
+                       int v = bytes[j] & 0xFF;
+                       hexChars[j * 2] = hexArray[v >>> 4];
+                       hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+               }
+               return new String(hexChars);
+       }
+
 }