/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nuxeo - initial API and implementation * * $Id$ */ package org.eclipse.ecr.core.api.repository.cache; import java.util.Collection; import java.util.HashSet; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.core.api.ClientException; import org.eclipse.ecr.core.api.DocumentModel; import org.eclipse.ecr.core.api.DocumentRef; import org.eclipse.ecr.core.api.operation.Modification; import org.eclipse.ecr.core.api.operation.OperationEvent; import org.eclipse.ecr.core.api.operation.OperationEventListener; /** * @author Max Stepanov */ public class DocumentModelCacheUpdater implements OperationEventListener { private static final Log log = LogFactory.getLog(DocumentModelCacheUpdater.class); private final DocumentModelCache cache;; public DocumentModelCacheUpdater(DocumentModelCache cache) { this.cache = cache; } protected List<DocumentModelCacheListener> listeners; @Override public void handleEvents(OperationEvent[] events, boolean urgent) { HashSet<DocumentModel> updatedDocs = new HashSet<DocumentModel>( events.length); HashSet<DocumentModel> updatedTrees = new HashSet<DocumentModel>( events.length); for (OperationEvent event : events) { try { handleEvent(cache, updatedDocs, updatedTrees, event); } catch (Exception e) { log.error("Exception handling event", e); } } if (!listeners.isEmpty()) { if (!updatedDocs.isEmpty()) { DocumentModel[] docs = updatedDocs.toArray(new DocumentModel[updatedDocs.size()]); for (DocumentModelCacheListener listener : listeners) { try { listener.documentsChanged(docs, urgent); } catch (Throwable error) { log.error("An error while trying fire listener for document modifications", error); } } } if (!updatedTrees.isEmpty()) { DocumentModel[] docs = updatedTrees.toArray(new DocumentModel[updatedTrees.size()]); for (DocumentModelCacheListener listener : listeners) { try { listener.subreeChanged(docs, urgent); } catch (Throwable error) { log.error("An error while trying fire listener for document modifications", error); } } } } } public void handleEvent(DocumentModelCache cache, HashSet<DocumentModel> updatedDocs, HashSet<DocumentModel> updatedTrees, OperationEvent event) throws ClientException { DocumentRef childRef = null; for (Modification modif : event.getModifications()) { DocumentModel doc = null; try { doc = cache.getCachedDocument(modif.ref); if (doc == null) { if (modif.isCreate()) { childRef = modif.ref; } else { if (log.isTraceEnabled()) { log.trace("Modif " + modif.ref + " [" + modif.type + "] - not in cache!"); } } continue; } if (modif.isContainerModification()) { if (log.isTraceEnabled()) { log.trace("Modif " + modif.ref + " [" + modif.type + "] - update children"); } if (modif.isOrderChild()) { childRef = null; } handleContainerModification(cache, updatedTrees, modif, doc, childRef); } else { childRef = null; } if (modif.isExistenceModification()) { if (log.isTraceEnabled()) { log.trace("Modif " + modif.ref + " [" + modif.type + "] - existence check"); } childRef = modif.ref; } if (modif.isUpdateModification()) { if (log.isTraceEnabled()) { log.trace("Modif " + modif.ref + " [" + modif.type + "] - update content"); } handleUpdateModification(cache, updatedDocs, doc); } } catch (Exception e) { StringBuilder sb = new StringBuilder("In event " + event.getId() + ", error handling "); if (modif.isCreate()) { sb.append("create "); } if (modif.isAddChild()) { sb.append("add-child "); } if (modif.isOrderChild()) { sb.append("order-child "); } if (modif.isRemove()) { sb.append("remove "); } if (modif.isRemoveChild()) { sb.append("remove-child "); } if (modif.isUpdateModification()) { sb.append("update "); } sb.append("modification " + modif); if (doc != null) { sb.append("\nCached DocumentModel '" + doc.getTitle() + "' [type=" + doc.getType() + " id=" + doc.getId() + " path=" + doc.getPathAsString() + "\n"); } else { sb.append("\nCached DocumentModel is null\n"); } sb.append("childref=" + childRef + "\n"); sb.append("\nWhilst processing the following Modification set:\n"); for (Modification m : event.getModifications()) { sb.append(m + (m == modif ? " (the cause)" : "") + "\n"); } sb.append("\nThis was caused by " + e.getMessage()); throw new ClientException(sb.toString(), e); } } } private void handleUpdateModification(DocumentModelCache cache, Collection<DocumentModel> updatedDocs, DocumentModel doc) throws ClientException { if (updatedDocs.add(doc)) { doc.refresh(); } } private void handleContainerModification(DocumentModelCache cache, Collection<DocumentModel> updatedTrees, Modification modif, DocumentModel doc, DocumentRef childRef) throws ClientException { if (childRef != null && modif.isRemoveChild()) { cache.uncacheDocument(childRef); cache.uncacheChild(doc.getRef(), childRef); updatedTrees.add(doc); return; } else if (childRef != null && modif.isAddChild()) { if (cache.getCachedChildren(doc.getRef()) != null) { DocumentModel model = null; if ((model = cache.fetchDocument(childRef)) != null) { if (model.getParentRef().equals(doc.getRef())) { cache.cacheChild(doc.getRef(), childRef); updatedTrees.add(doc); } } } return; } else if (childRef != null) { cache.uncacheChildren(doc.getRef()); updatedTrees.add(doc); return; } if (cache.getCachedChildren(doc.getRef()) != null) { cache.fetchAndCacheChildren(doc.getRef()); updatedTrees.add(doc); } } }