X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fkernel%2FCmsWorkspaceIndexer.java;h=bb38d6a633c02b33e4b0ba76b6cca336ff8aeb8e;hb=fb22feb37b0c2340d3d846dce4b6f47d0f728efb;hp=20ced323091024ca139a5f5400777117b8ec7e47;hpb=eb3116df3624b3d32793548b79e137e2dad429cb;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsWorkspaceIndexer.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsWorkspaceIndexer.java index 20ced3230..bb38d6a63 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsWorkspaceIndexer.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsWorkspaceIndexer.java @@ -1,6 +1,8 @@ package org.argeo.cms.internal.kernel; import java.util.GregorianCalendar; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.atomic.AtomicBoolean; import javax.jcr.Node; import javax.jcr.Property; @@ -12,6 +14,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 +26,23 @@ 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; + + private LinkedBlockingDeque toProcess = new LinkedBlockingDeque<>(); + private IndexingThread indexingThread; + private AtomicBoolean stopping = new AtomicBoolean(false); public CmsWorkspaceIndexer(RepositoryImpl repositoryImpl, String cn, String workspaceName) throws RepositoryException { @@ -45,109 +56,194 @@ 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(); + + indexingThread = new IndexingThread(); + indexingThread.start(); } catch (RepositoryException e1) { throw new IllegalStateException(e1); } } public void destroy() { + stopping.set(true); + indexingThread.interrupt(); + // TODO make it configurable + try { + indexingThread.join(10 * 60 * 1000); + } catch (InterruptedException e1) { + log.warn("Indexing thread interrupted. Will log out session."); + } + 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); + try { + toProcess.put(event); + } catch (InterruptedException e) { + e.printStackTrace(); + } +// 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() { - - @Override - public void run() { - processEvents(events); - } - }; - Activator.getInternalExecutorService().execute(toRun); + processEvents(events); +// Runnable toRun = new Runnable() { +// +// @Override +// public void run() { +// processEvents(events); +// } +// }; +// 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 +256,88 @@ 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; + } + + class IndexingThread extends Thread { + + public IndexingThread() { + super(CmsWorkspaceIndexer.this.toString()); + // TODO Auto-generated constructor stub + } + + @Override + public void run() { + life: while (session != null && session.isLive()) { + try { + Event nextEvent = toProcess.take(); + processEvent(nextEvent); + } catch (InterruptedException e) { + // silent + interrupted(); + } + + if (stopping.get() && toProcess.isEmpty()) { + break life; + } + } + if (log.isDebugEnabled()) + log.debug(CmsWorkspaceIndexer.this.toString() + " has shut down."); + } + } } \ No newline at end of file