Improve integration.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / CmsWorkspaceIndexer.java
index 20ced323091024ca139a5f5400777117b8ec7e47..7782b4291f5122d9b72a2d85f0cd93fd7c170b1c 100644 (file)
@@ -1,6 +1,10 @@
 package org.argeo.cms.internal.kernel;
 
 import java.util.GregorianCalendar;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.jcr.Node;
 import javax.jcr.Property;
@@ -12,6 +16,7 @@ import javax.jcr.nodetype.NodeType;
 import javax.jcr.observation.Event;
 import javax.jcr.observation.EventIterator;
 import javax.jcr.observation.EventListener;
+import javax.jcr.version.VersionManager;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -23,15 +28,19 @@ import org.argeo.jcr.JcrUtils;
 class CmsWorkspaceIndexer implements EventListener {
        private final static Log log = LogFactory.getLog(CmsWorkspaceIndexer.class);
 
-       private final static String MIX_ETAG = "mix:etag";
+//     private final static String MIX_ETAG = "mix:etag";
        private final static String JCR_ETAG = "jcr:etag";
-       private final static String JCR_LAST_MODIFIED = "jcr:lastModified";
-       private final static String JCR_LAST_MODIFIED_BY = "jcr:lastModifiedBy";
+//     private final static String JCR_LAST_MODIFIED = "jcr:lastModified";
+//     private final static String JCR_LAST_MODIFIED_BY = "jcr:lastModifiedBy";
+//     private final static String JCR_MIXIN_TYPES = "jcr:mixinTypes";
        private final static String JCR_DATA = "jcr:data";
-       String cn;
-       String workspaceName;
-       RepositoryImpl repositoryImpl;
-       Session session;
+       private final static String JCR_CONTENT = "jcr:data";
+
+       private String cn;
+       private String workspaceName;
+       private RepositoryImpl repositoryImpl;
+       private Session session;
+       private VersionManager versionManager;
 
        public CmsWorkspaceIndexer(RepositoryImpl repositoryImpl, String cn, String workspaceName)
                        throws RepositoryException {
@@ -45,7 +54,8 @@ class CmsWorkspaceIndexer implements EventListener {
                try {
                        String[] nodeTypes = { NodeType.NT_FILE, NodeType.MIX_LAST_MODIFIED };
                        session.getWorkspace().getObservationManager().addEventListener(this,
-                                       Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_CHANGED, "/", true, null, nodeTypes, true);
+                                       Event.NODE_ADDED | Event.PROPERTY_CHANGED, "/", true, null, nodeTypes, true);
+                       versionManager = session.getWorkspace().getVersionManager();
                } catch (RepositoryException e1) {
                        throw new IllegalStateException(e1);
                }
@@ -55,89 +65,149 @@ class CmsWorkspaceIndexer implements EventListener {
                try {
                        session.getWorkspace().getObservationManager().removeEventListener(this);
                } catch (RepositoryException e) {
-                       log.warn("Cannot unregistered JCR event listener", e);
+                       if (log.isTraceEnabled())
+                               log.warn("Cannot unregistered JCR event listener", e);
                } finally {
                        JcrUtils.logoutQuietly(session);
                }
        }
 
        private synchronized void processEvents(EventIterator events) {
+               long begin = System.currentTimeMillis();
+               long count = 0;
                while (events.hasNext()) {
                        Event event = events.nextEvent();
                        processEvent(event);
+                       count++;
                }
+               long duration = System.currentTimeMillis() - begin;
+               if (log.isTraceEnabled())
+                       log.trace("Processed " + count + " events in " + duration + " ms");
                notifyAll();
        }
 
        protected synchronized void processEvent(Event event) {
                try {
+                       String eventPath = event.getPath();
                        if (event.getType() == Event.NODE_ADDED) {
-                               session.refresh(true);
-                               Node node = session.getNode(event.getPath());
-                               if (node.getParent().isNodeType(NodeType.NT_FILE)) {
+                               if (!versionManager.isCheckedOut(eventPath))
+                                       return;// ignore checked-in nodes
+                               if (log.isTraceEnabled())
+                                       log.trace("NODE_ADDED " + eventPath);
+//                             session.refresh(true);
+                               session.refresh(false);
+                               Node node = session.getNode(eventPath);
+                               Node parentNode = node.getParent();
+                               if (parentNode.isNodeType(NodeType.NT_FILE)) {
                                        if (node.isNodeType(NodeType.NT_UNSTRUCTURED)) {
                                                if (!node.isNodeType(NodeType.MIX_LAST_MODIFIED))
                                                        node.addMixin(NodeType.MIX_LAST_MODIFIED);
                                                Property property = node.getProperty(Property.JCR_DATA);
                                                String etag = toEtag(property.getValue());
-                                               node.setProperty(JCR_ETAG, etag);
-                                       } else if (node.isNodeType(NodeType.NT_RESOURCE)) {
-                                               if (!node.isNodeType(MIX_ETAG))
-                                                       node.addMixin(MIX_ETAG);
                                                session.save();
-                                               Property property = node.getProperty(Property.JCR_DATA);
-                                               String etag = toEtag(property.getValue());
                                                node.setProperty(JCR_ETAG, etag);
-                                               session.save();
+                                               if (log.isTraceEnabled())
+                                                       log.trace("ETag and last modified added to new " + node);
+                                       } else if (node.isNodeType(NodeType.NT_RESOURCE)) {
+//                                             if (!node.isNodeType(MIX_ETAG))
+//                                                     node.addMixin(MIX_ETAG);
+//                                             session.save();
+//                                             Property property = node.getProperty(Property.JCR_DATA);
+//                                             String etag = toEtag(property.getValue());
+//                                             node.setProperty(JCR_ETAG, etag);
+//                                             session.save();
                                        }
-                                       setLastModified(node.getParent(), event);
-                                       session.save();
-                                       if (log.isDebugEnabled())
-                                               log.debug("ETag and last modified added to new " + node);
+//                                     setLastModifiedRecursive(parentNode, event);
+//                                     session.save();
+//                                     if (log.isTraceEnabled())
+//                                             log.trace("ETag and last modified added to new " + node);
                                }
+
+//                             if (node.isNodeType(NodeType.NT_FOLDER)) {
+//                                     setLastModifiedRecursive(node, event);
+//                                     session.save();
+//                                     if (log.isTraceEnabled())
+//                                             log.trace("Last modified added to new " + node);
+//                             }
                        } else if (event.getType() == Event.PROPERTY_CHANGED) {
-                               if (!session.propertyExists(event.getPath()))
-                                       return;
-                               Property property = session.getProperty(event.getPath());
-                               String propertyName = property.getName();
+                               String propertyName = extractItemName(eventPath);
                                // skip if last modified properties are explicitly set
-                               if (propertyName.equals(JCR_LAST_MODIFIED))
+                               if (!propertyName.equals(JCR_DATA))
                                        return;
-                               if (propertyName.equals(JCR_LAST_MODIFIED_BY))
+//                             if (propertyName.equals(JCR_LAST_MODIFIED))
+//                                     return;
+//                             if (propertyName.equals(JCR_LAST_MODIFIED_BY))
+//                                     return;
+//                             if (propertyName.equals(JCR_MIXIN_TYPES))
+//                                     return;
+//                             if (propertyName.equals(JCR_ETAG))
+//                                     return;
+
+                               if (log.isTraceEnabled())
+                                       log.trace("PROPERTY_CHANGED " + eventPath);
+
+                               if (!session.propertyExists(eventPath))
                                        return;
+                               session.refresh(false);
+                               Property property = session.getProperty(eventPath);
                                Node node = property.getParent();
                                if (property.getType() == PropertyType.BINARY && propertyName.equals(JCR_DATA)
                                                && node.isNodeType(NodeType.NT_UNSTRUCTURED)) {
                                        String etag = toEtag(property.getValue());
                                        node.setProperty(JCR_ETAG, etag);
+                                       Node parentNode = node.getParent();
+                                       if (parentNode.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
+                                               setLastModified(parentNode, event);
+                                       }
+                                       if (log.isTraceEnabled())
+                                               log.trace("ETag and last modified updated for " + node);
                                }
-                               setLastModified(node, event);
-                               session.save();
-                               if (log.isDebugEnabled())
-                                       log.debug("ETag and last modified updated for " + node);
+//                             setLastModified(node, event);
+//                             session.save();
+//                             if (log.isTraceEnabled())
+//                                     log.trace("ETag and last modified updated for " + node);
                        } else if (event.getType() == Event.NODE_REMOVED) {
-                               String removeNodePath = event.getPath();
-                               String parentPath = JcrUtils.parentPath(removeNodePath);
-                               session.refresh(true);
-                               setLastModified(session, parentPath, event);
-                               session.save();
-                               if (log.isDebugEnabled())
-                                       log.debug("Last modified updated for parents of removed " + removeNodePath);
+                               String removeNodePath = eventPath;
+                               String nodeName = extractItemName(eventPath);
+                               if (JCR_CONTENT.equals(nodeName)) // parent is a file, deleted anyhow
+                                       return;
+                               if (log.isTraceEnabled())
+                                       log.trace("NODE_REMOVED " + eventPath);
+//                             String parentPath = JcrUtils.parentPath(removeNodePath);
+//                             session.refresh(true);
+//                             setLastModified(parentPath, event);
+//                             session.save();
+                               if (log.isTraceEnabled())
+                                       log.trace("Last modified updated for parents of removed " + removeNodePath);
                        }
                } catch (Exception e) {
-                       log.warn("Cannot process event " + event, e);
+                       if (log.isTraceEnabled())
+                               log.warn("Cannot process event " + event, e);
                } finally {
-                       try {
-                               if (session.hasPendingChanges())
-                                       session.save();
-//                             session.refresh(false);
-                       } catch (RepositoryException e) {
-                               log.warn("Cannot refresh JCR session", e);
-                       }
+//                     try {
+//                             session.refresh(true);
+//                             if (session.hasPendingChanges())
+//                                     session.save();
+////                           session.refresh(false);
+//                     } catch (RepositoryException e) {
+//                             if (log.isTraceEnabled())
+//                                     log.warn("Cannot refresh JCR session", e);
+//                     }
                }
 
        }
 
+       private String extractItemName(String path) {
+               if (path == null || path.length() <= 1)
+                       return null;
+               int lastIndex = path.lastIndexOf('/');
+               if (lastIndex >= 0) {
+                       return path.substring(lastIndex + 1);
+               } else {
+                       return path;
+               }
+       }
+
        @Override
        public void onEvent(EventIterator events) {
                Runnable toRun = new Runnable() {
@@ -147,7 +217,13 @@ class CmsWorkspaceIndexer implements EventListener {
                                processEvents(events);
                        }
                };
-               Activator.getInternalExecutorService().execute(toRun);
+               Future<?> future = Activator.getInternalExecutorService().submit(toRun);
+               try {
+                       // make the call synchronous
+                       future.get(60, TimeUnit.SECONDS);
+               } catch (TimeoutException | ExecutionException | InterruptedException e) {
+                       // silent
+               }
        }
 
        static String toEtag(Value v) {
@@ -160,46 +236,60 @@ class CmsWorkspaceIndexer implements EventListener {
 
        }
 
-       /** Recursively set the last updated time on parents. */
-       static void setLastModified(Node node, Event event) throws RepositoryException {
+       protected synchronized void setLastModified(Node node, Event event) throws RepositoryException {
                GregorianCalendar calendar = new GregorianCalendar();
                calendar.setTimeInMillis(event.getDate());
-               if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
-                       node.setProperty(Property.JCR_LAST_MODIFIED, calendar);
-                       node.setProperty(Property.JCR_LAST_MODIFIED_BY, event.getUserID());
-               }
-               if (node.isNodeType(NodeType.NT_FOLDER) && !node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
-                       node.addMixin(NodeType.MIX_LAST_MODIFIED);
-               }
+               node.setProperty(Property.JCR_LAST_MODIFIED, calendar);
+               node.setProperty(Property.JCR_LAST_MODIFIED_BY, event.getUserID());
+               if (log.isTraceEnabled())
+                       log.trace("Last modified set on " + node);
+       }
+
+       /** Recursively set the last updated time on parents. */
+       protected synchronized void setLastModifiedRecursive(Node node, Event event) throws RepositoryException {
+               if (versionManager.isCheckedOut(node.getPath())) {
+                       if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
+                               setLastModified(node, event);
+                       }
+                       if (node.isNodeType(NodeType.NT_FOLDER) && !node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
+                               node.addMixin(NodeType.MIX_LAST_MODIFIED);
+                               if (log.isTraceEnabled())
+                                       log.trace("Last modified mix-in added to " + node);
+                       }
 
-               try {
-                       node.getSession().save();
-               } catch (RepositoryException e) {
-                       // fail silently and keep recursing
                }
-               if (node.getDepth() == 0)
+
+               // end condition
+               if (node.getDepth() == 0) {
+//                     try {
+//                             node.getSession().save();
+//                     } catch (RepositoryException e) {
+//                             log.warn("Cannot index workspace", e);
+//                     }
                        return;
-               Node parent = node.getParent();
-               setLastModified(parent, event);
+               } else {
+                       Node parent = node.getParent();
+                       setLastModifiedRecursive(parent, event);
+               }
        }
 
        /**
         * Recursively set the last updated time on parents. Useful to use paths when
         * dealing with deletions.
         */
-       static void setLastModified(Session session, String path, Event event) throws RepositoryException {
+       protected synchronized void setLastModifiedRecursive(String path, Event event) throws RepositoryException {
                // root node will always exist, so end condition is delegated to the other
                // recursive setLastModified method
                if (session.nodeExists(path)) {
-                       setLastModified(session.getNode(path), event);
+                       setLastModifiedRecursive(session.getNode(path), event);
                } else {
-                       setLastModified(session, JcrUtils.parentPath(path), event);
+                       setLastModifiedRecursive(JcrUtils.parentPath(path), event);
                }
        }
 
        @Override
        public String toString() {
-               return "Monitor workspace " + workspaceName + " of repository " + cn;
+               return "Indexer for workspace " + workspaceName + " of repository " + cn;
        }
 
 }
\ No newline at end of file