]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java
Fix typo in date path generation
[lgpl/argeo-commons.git] / server / runtime / org.argeo.server.jcr / src / main / java / org / argeo / jcr / JcrUtils.java
index 77309feea0ad8e88c507b81f279184ac3b750373..cf0d2d74aa9bc7adf0b273ef0b0d9b845d6f4b71 100644 (file)
@@ -41,7 +41,9 @@ import javax.jcr.RepositoryException;
 import javax.jcr.RepositoryFactory;
 import javax.jcr.Session;
 import javax.jcr.Value;
+import javax.jcr.Workspace;
 import javax.jcr.nodetype.NodeType;
+import javax.jcr.observation.EventListener;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.qom.Constraint;
@@ -158,25 +160,26 @@ public class JcrUtils implements ArgeoJcrConstants {
         */
        public static String dateAsPath(Calendar cal, Boolean addHour) {
                StringBuffer buf = new StringBuffer(14);
-               buf.append('Y').append(cal.get(Calendar.YEAR));// 5
-               buf.append('/');// 1
+               buf.append('Y').append(cal.get(Calendar.YEAR));
+               buf.append('/');
                int month = cal.get(Calendar.MONTH) + 1;
                buf.append('M');
                if (month < 10)
                        buf.append(0);
-               buf.append(month);// 3
-               buf.append('/');// 1
+               buf.append(month);
+               buf.append('/');
                int day = cal.get(Calendar.DAY_OF_MONTH);
                if (day < 10)
                        buf.append(0);
-               buf.append('D').append(day);// 3
-               buf.append('/');// 1
+               buf.append('D').append(day);
+               buf.append('/');
                if (addHour) {
                        int hour = cal.get(Calendar.HOUR_OF_DAY);
+                       buf.append('H');
                        if (hour < 10)
                                buf.append(0);
-                       buf.append('H').append(hour);// 3
-                       buf.append('/');// 1
+                       buf.append(hour);
+                       buf.append('/');
                }
                return buf.toString();
 
@@ -325,6 +328,11 @@ public class JcrUtils implements ArgeoJcrConstants {
 
        /** Recursively outputs the contents of the given node. */
        public static void debug(Node node) {
+               debug(node, log);
+       }
+
+       /** Recursively outputs the contents of the given node. */
+       public static void debug(Node node, Log log) {
                try {
                        // First output the node path
                        log.debug(node.getPath());
@@ -365,11 +373,16 @@ public class JcrUtils implements ArgeoJcrConstants {
        }
 
        /**
-        * Copies recursively the content of a node to another one. Mixin are NOT
-        * copied.
+        * Copies recursively the content of a node to another one. Do NOT copy the
+        * property values of {@link NodeType#MIX_CREATED} and
+        * {@link NodeType#MIX_LAST_MODIFIED}, but update the
+        * {@link Property#JCR_LAST_MODIFIED} and
+        * {@link Property#JCR_LAST_MODIFIED_BY} properties if the target node has
+        * the {@link NodeType#MIX_LAST_MODIFIED} mixin.
         */
        public static void copy(Node fromNode, Node toNode) {
                try {
+                       // process properties
                        PropertyIterator pit = fromNode.getProperties();
                        properties: while (pit.hasNext()) {
                                Property fromProperty = pit.nextProperty();
@@ -379,10 +392,32 @@ public class JcrUtils implements ArgeoJcrConstants {
                                                                .isProtected())
                                        continue properties;
 
-                               toNode.setProperty(fromProperty.getName(),
-                                               fromProperty.getValue());
+                               if (fromProperty.getDefinition().isProtected())
+                                       continue properties;
+
+                               if (propertyName.equals("jcr:created")
+                                               || propertyName.equals("jcr:createdBy")
+                                               || propertyName.equals("jcr:lastModified")
+                                               || propertyName.equals("jcr:lastModifiedBy"))
+                                       continue properties;
+
+                               if (fromProperty.isMultiple()) {
+                                       toNode.setProperty(propertyName, fromProperty.getValues());
+                               } else {
+                                       toNode.setProperty(propertyName, fromProperty.getValue());
+                               }
                        }
 
+                       // update jcr:lastModified and jcr:lastModifiedBy in toNode in case
+                       // they existed, before adding the mixins
+                       updateLastModified(toNode);
+
+                       // add mixins
+                       for (NodeType mixinType : fromNode.getMixinNodeTypes()) {
+                               toNode.addMixin(mixinType.getName());
+                       }
+
+                       // process children nodes
                        NodeIterator nit = fromNode.getNodes();
                        while (nit.hasNext()) {
                                Node fromChild = nit.nextNode();
@@ -623,6 +658,21 @@ public class JcrUtils implements ArgeoJcrConstants {
                }
        }
 
+       /**
+        * Discards the current changes in the session attached to this node. To be
+        * used typically in a catch block.
+        * 
+        * @see #discardQuietly(Session)
+        */
+       public static void discardUnderlyingSessionQuietly(Node node) {
+               try {
+                       discardQuietly(node.getSession());
+               } catch (RepositoryException e) {
+                       log.warn("Cannot quietly discard session of node " + node + ": "
+                                       + e.getMessage());
+               }
+       }
+
        /**
         * Discards the current changes in a session by calling
         * {@link Session#refresh(boolean)} with <code>false</code>, only logging
@@ -650,6 +700,24 @@ public class JcrUtils implements ArgeoJcrConstants {
                return getUserHome(session, userID);
        }
 
+       /**
+        * Returns user home has path, embedding exceptions. Contrary to
+        * {@link #getUserHome(Session)}, it never returns null but throws and
+        * exception if not found.
+        */
+       public static String getUserHomePath(Session session) {
+               String userID = session.getUserID();
+               try {
+                       Node userHome = getUserHome(session, userID);
+                       if (userHome != null)
+                               return userHome.getPath();
+                       else
+                               throw new ArgeoException("No home registered for " + userID);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot find user home path", e);
+               }
+       }
+
        /** Get the profile of the user attached to this session. */
        public static Node getUserProfile(Session session) {
                String userID = session.getUserID();
@@ -663,10 +731,10 @@ public class JcrUtils implements ArgeoJcrConstants {
         *            the session to use in order to perform the search, this can be
         *            a session with a different user ID than the one searched,
         *            typically when a system or admin session is used.
-        * @param userID
-        *            the id of the user
+        * @param username
+        *            the username of the user
         */
-       public static Node getUserHome(Session session, String userID) {
+       public static Node getUserHome(Session session, String username) {
                try {
                        QueryObjectModelFactory qomf = session.getWorkspace()
                                        .getQueryManager().getQOMFactory();
@@ -677,18 +745,18 @@ public class JcrUtils implements ArgeoJcrConstants {
                        DynamicOperand userIdDop = qomf.propertyValue("userHome",
                                        ArgeoNames.ARGEO_USER_ID);
                        StaticOperand userIdSop = qomf.literal(session.getValueFactory()
-                                       .createValue(userID));
+                                       .createValue(username));
                        Constraint constraint = qomf.comparison(userIdDop,
                                        QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
                        Query query = qomf.createQuery(userHomeSel, constraint, null, null);
                        Node userHome = JcrUtils.querySingleNode(query);
                        return userHome;
                } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot find home for user " + userID, e);
+                       throw new ArgeoException("Cannot find home for user " + username, e);
                }
        }
 
-       public static Node getUserProfile(Session session, String userID) {
+       public static Node getUserProfile(Session session, String username) {
                try {
                        QueryObjectModelFactory qomf = session.getWorkspace()
                                        .getQueryManager().getQOMFactory();
@@ -697,21 +765,24 @@ public class JcrUtils implements ArgeoJcrConstants {
                        DynamicOperand userIdDop = qomf.propertyValue("userProfile",
                                        ArgeoNames.ARGEO_USER_ID);
                        StaticOperand userIdSop = qomf.literal(session.getValueFactory()
-                                       .createValue(userID));
+                                       .createValue(username));
                        Constraint constraint = qomf.comparison(userIdDop,
                                        QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
                        Query query = qomf.createQuery(sel, constraint, null, null);
                        Node userHome = JcrUtils.querySingleNode(query);
                        return userHome;
                } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot find profile for user " + userID,
-                                       e);
+                       throw new ArgeoException(
+                                       "Cannot find profile for user " + username, e);
                }
        }
 
+       /** Creates an Argeo user home. */
        public static Node createUserHome(Session session, String homeBasePath,
                        String username) {
                try {
+                       if (session == null)
+                               throw new ArgeoException("Session is null");
                        if (session.hasPendingChanges())
                                throw new ArgeoException(
                                                "Session has pending changes, save them first");
@@ -724,11 +795,6 @@ public class JcrUtils implements ArgeoJcrConstants {
                        userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
                        session.save();
                        // we need to save the profile before adding the user home type
-                       PropertyIterator pit = userHome.getProperties();
-                       while (pit.hasNext()) {
-                               Property p = pit.nextProperty();
-                               log.debug(p.getName() + "=" + p.getValue().getString());
-                       }
                        userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
                        // see
                        // http://jackrabbit.510166.n4.nabble.com/Jackrabbit-2-0-beta-6-Problem-adding-a-Mixin-type-with-mandatory-properties-after-setting-propertiesn-td1290332.html
@@ -741,4 +807,57 @@ public class JcrUtils implements ArgeoJcrConstants {
                                        + username, e);
                }
        }
+
+       /**
+        * Quietly unregisters an {@link EventListener} from the udnerlying
+        * workspace of this node.
+        */
+       public static void unregisterQuietly(Node node, EventListener eventListener) {
+               try {
+                       unregisterQuietly(node.getSession().getWorkspace(), eventListener);
+               } catch (RepositoryException e) {
+                       // silent
+                       if (log.isTraceEnabled())
+                               log.trace("Could not unregister event listener "
+                                               + eventListener);
+               }
+       }
+
+       /** Quietly unregisters an {@link EventListener} from this workspace */
+       public static void unregisterQuietly(Workspace workspace,
+                       EventListener eventListener) {
+               if (eventListener == null)
+                       return;
+               try {
+                       workspace.getObservationManager()
+                                       .removeEventListener(eventListener);
+               } catch (RepositoryException e) {
+                       // silent
+                       if (log.isTraceEnabled())
+                               log.trace("Could not unregister event listener "
+                                               + eventListener);
+               }
+       }
+
+       /**
+        * If this node is has the {@link NodeType#MIX_LAST_MODIFIED} mixin, it
+        * updates the {@link Property#JCR_LAST_MODIFIED} property with the current
+        * time and the {@link Property#JCR_LAST_MODIFIED_BY} property with the
+        * underlying session user id. In Jackrabbit 2.x, <a
+        * href="https://issues.apache.org/jira/browse/JCR-2233">these properties
+        * are not automatically updated</a>, hence the need for manual update. The
+        * session is not saved.
+        */
+       public static void updateLastModified(Node node) {
+               try {
+                       if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
+                               node.setProperty(Property.JCR_LAST_MODIFIED,
+                                               new GregorianCalendar());
+                               node.setProperty(Property.JCR_LAST_MODIFIED_BY, node
+                                               .getSession().getUserID());
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot update last modified", e);
+               }
+       }
 }