[Q&A Friday] How can you know which document properties have been modified?


Fri 23 November 2012 By Laurent Doguin


How can you know which document properties have been modified? How can you know which document properties have been modified?

A question that's coming now and then is how do you know which document properties have been changed? I remember someone asking that on Nuxeo Answers but I can't find the question anymore.

Anyway this can be useful for many things. First example would be thumbnail generation. The thumb is computed from the main document file. If this file changes, we want to update the thumb. To be aware of that change, you need to listen to the right event.

When you save modifications on a doc, several events occur. Some of the most interesting for us are beforeDocumentModification and aboutToCreate. It's the perfect time to know what properties are going to be modified. These events context are instances of DocumentEventContext, which means we can retrieve the source document where the event occurred. All you have to do next is introspect the document properties to know which one have been modified. Here's a listener to test this:

/*

  • (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and contributors.
    *
  • All rights reserved. This program and the accompanying materials
  • are made available under the terms of the GNU Lesser General Public License
  • (LGPL) version 2.1 which accompanies this distribution, and is available at
  • http://www.gnu.org/licenses/lgpl.html
    *
  • This library is distributed in the hope that it will be useful,
  • but WITHOUT ANY WARRANTY; without even the implied warranty of
  • MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  • Lesser General Public License for more details.
    *
  • Contributors:
  • ldoguin
    */
    package org.nuxeo.ecm.core.event.test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.model.DocumentPart;
import org.nuxeo.ecm.core.api.model.Property;
import org.nuxeo.ecm.core.event.Event;
import org.nuxeo.ecm.core.event.EventListener;
import org.nuxeo.ecm.core.event.impl.DocumentEventContext;

/**

  • @author ldoguin
    */
    public class DirtyPropGetterTestListener implements EventListener {

private static final Log log = LogFactory.getLog(DirtyPropGetterTestListener.class);

public void handleEvent(Event event) throws ClientException {
if (event.getContext() instanceof DocumentEventContext) {
DocumentEventContext context = (DocumentEventContext) event.getContext();
DocumentModel doc = context.getSourceDocument();
List<String> lastDirtyFields = getDirtyPropertiesXPath(doc);
for (String xpath : lastDirtyFields) {
log.debug(xpath);
}
}
}

public List<String> getDirtyPropertiesXPath(DocumentModel doc)
throws ClientException {
List<String> dirtyPropertiesName = new ArrayList<String>();
DocumentPart[] docParts = doc.getParts();
for (DocumentPart docPart : docParts) {
Iterator<Property> dirtyChildrenIterator = docPart.getDirtyChildren();
while (dirtyChildrenIterator.hasNext()) {
Property property = dirtyChildrenIterator.next();
if (!property.isContainer() && property.isDirty()) {
dirtyPropertiesName.add(docPart.getName() + ":"

  • property.getField().getName().getLocalName());
    } else {
    List<Property> dirtyProps = addChildrenDirtyProperties(
    property, new ArrayList<Property>());
    for (Property dirtyProperty : dirtyProps) {
    dirtyPropertiesName.add(docPart.getName() + ":"
  • dirtyProperty.getPath().substring(1));
    }
    }
    }
    }
    return dirtyPropertiesName;
    }

private List<Property> addChildrenDirtyProperties(Property property,
List<Property> dirtyProperties) {
if (!property.isContainer() && property.isDirty()) {
dirtyProperties.add(property);
return dirtyProperties;
} else {
Iterator<Property> dirtyChildrenIterator = property.getDirtyChildren();
while (dirtyChildrenIterator.hasNext()) {
Property chilProperty = dirtyChildrenIterator.next();
dirtyProperties = addChildrenDirtyProperties(chilProperty,
dirtyProperties);
}
return dirtyProperties;
}
}
}


What we do here is verify that the event context is indeed a DocumentEventContext, cast it and retrieve the event's source document from it. Once we have the document, we can start introspecting its DocumentPart. It's basically a schema representation from which you can retrieve every properties and check if they are dirty or not. Then we store the properties xPath in a list and log them with the debug level. Don't forget the XML declaration for your listener:

<?xml version="1.0"?>
<component name="test-dirtyProplistener">
<extension target="org.nuxeo.ecm.core.event.EventServiceComponent"
point="listener">
<listener async="false" postCommit="false"
class="org.nuxeo.ecm.core.event.test.DirtyPropGetterTestListener"
name="testDirtyPropertiesGetter">
<event>beforeDocumentModification</event>
<event>aboutToCreate</event>
</listener>
</extension>
</component>

FYI, this is going to be added to the DocumentEventContext in Nuxeo 5.7 and is already in the trunk. That's it for today, see you Monday :)


Category: Product & Development
Tagged: Java, Q&A