1 package org.collectionspace.services.listener;
3 import java.util.ArrayList;
4 import java.util.Iterator;
7 import org.apache.commons.collections.ListUtils;
8 import org.apache.commons.lang3.StringUtils;
9 import org.collectionspace.services.common.api.RefNameUtils;
10 import org.collectionspace.services.nuxeo.listener.AbstractCSEventPostCommitListenerImpl;
11 import org.nuxeo.ecm.core.api.CoreSession;
12 import org.nuxeo.ecm.core.api.DocumentModel;
13 import org.nuxeo.ecm.core.api.DocumentModelList;
14 import org.nuxeo.ecm.core.api.event.DocumentEventTypes;
15 import org.nuxeo.ecm.core.event.Event;
16 import org.nuxeo.ecm.core.event.EventBundle;
17 import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
18 import org.nuxeo.elasticsearch.ElasticSearchComponent;
19 import org.nuxeo.elasticsearch.api.ElasticSearchService;
20 import org.nuxeo.runtime.api.Framework;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
25 * Event listener that triggers reindexing of records in Elasticsearch when an associated record
26 * is created/updated/deleted. When a record is created or updated Nuxeo will automatically
27 * reindex it in ElasticSearch, but Nuxeo does not know about other records that may also need to
28 * be reindexed; for example, if a related record denormalizes data from the updated record at
31 public class Reindex extends AbstractCSEventPostCommitListenerImpl {
32 private static final Logger logger = LoggerFactory.getLogger(Reindex.class);
34 // This listener runs asynchronously post-commit, so that reindexing records after a
35 // save does not hold up the save.
37 public static final String PREV_COVERAGE_KEY = "Reindex.PREV_COVERAGE";
38 public static final String PREV_CREDIT_LINE_KEY = "Reindex.PREV_CREDIT_LINE";
39 public static final String PREV_PUBLISH_TO_KEY = "Reindex.PREV_PUBLISH_TO";
40 public static final String PREV_RELATED_COLLECTION_OBJECT_CSID_KEY = "Reindex.PREV_RELATED_COLLECTION_OBJECT_CSID";
41 public static final String ELASTICSEARCH_ENABLED_PROP = "elasticsearch.enabled";
44 public boolean shouldHandleEventBundle(EventBundle eventBundle) {
45 if (Framework.isBooleanPropertyTrue(ELASTICSEARCH_ENABLED_PROP) && eventBundle.size() > 0) {
53 public boolean shouldHandleEvent(Event event) {
54 DocumentEventContext eventContext = (DocumentEventContext) event.getContext();
55 DocumentModel doc = eventContext.getSourceDocument();
56 String docType = doc.getType();
59 docType.startsWith("Media")
60 || docType.startsWith("Relation")
61 || docType.startsWith("Acquisition")
70 @SuppressWarnings("unchecked")
71 public void handleCSEvent(Event event) {
72 DocumentEventContext eventContext = (DocumentEventContext) event.getContext();
73 DocumentModel doc = eventContext.getSourceDocument();
74 String docType = doc.getType();
75 String eventName = event.getName();
77 // TODO: Make this configurable. This is currently hardcoded to the needs of the standard
80 if (docType.startsWith("Media")) {
81 // When a media record is created, reindex the material item that is referenced by its
84 // When a media record is updated and the coverage changed, reindex both the old and new
85 // referenced material items.
87 // When a media record is deleted, reindex the material item that was referenced by its
91 eventName.equals(DocumentEventTypes.DOCUMENT_CREATED) ||
92 eventName.equals(DocumentEventTypes.DOCUMENT_UPDATED)
94 String prevCoverage = (String) eventContext.getProperty(PREV_COVERAGE_KEY);
95 String coverage = (String) doc.getProperty("media_common", "coverage");
97 List<String> prevPublishTo = (List<String>) eventContext.getProperty(PREV_PUBLISH_TO_KEY);
99 // Materials profile had publishToList defined in a local extension schema before
100 // that field was added to the common schema.
102 List<String> publishTo = (List<String>) doc.getProperty(
103 doc.hasSchema("media_materials") ? "media_materials" : "media_common",
107 !ListUtils.isEqualList(prevPublishTo, publishTo) ||
108 !StringUtils.equals(prevCoverage, coverage)
110 if (!StringUtils.equals(prevCoverage, coverage)) {
111 reindexMaterial(doc.getRepositoryName(), prevCoverage);
114 reindexMaterial(doc.getRepositoryName(), coverage);
116 if (!ListUtils.isEqualList(prevPublishTo, publishTo)) {
117 reindexRelatedCollectionObjects(doc);
121 else if (eventName.equals("lifecycle_transition_event") && doc.getCurrentLifeCycleState().equals("deleted")) {
122 String coverage = (String) doc.getProperty("media_common", "coverage");
124 reindexMaterial(doc.getRepositoryName(), coverage);
126 else if (eventName.equals(DocumentEventTypes.DOCUMENT_REMOVED)) {
127 String prevCoverage = (String) eventContext.getProperty(PREV_COVERAGE_KEY);
129 reindexMaterial(doc.getRepositoryName(), prevCoverage);
130 reindexPrevRelatedCollectionObjects(eventContext);
133 else if (docType.startsWith("Acquisition")) {
134 if (eventName.equals(DocumentEventTypes.DOCUMENT_UPDATED)) {
135 String prevCreditLine = (String) eventContext.getProperty(PREV_CREDIT_LINE_KEY);
136 String creditLine = (String) doc.getProperty("acquisitions_common", "creditLine");
138 if (!StringUtils.equals(prevCreditLine, creditLine)) {
139 reindexRelatedCollectionObjects(doc);
142 else if (eventName.equals(DocumentEventTypes.DOCUMENT_REMOVED)) {
143 reindexPrevRelatedCollectionObjects(eventContext);
146 else if (docType.startsWith("Relation")) {
148 eventName.equals(DocumentEventTypes.DOCUMENT_CREATED)
149 || (eventName.equals("lifecycle_transition_event") && doc.getCurrentLifeCycleState().equals("deleted"))
151 String subjectDocumentType = (String) doc.getProperty("relations_common", "subjectDocumentType");
152 String objectDocumentType = (String) doc.getProperty("relations_common", "objectDocumentType");
155 (subjectDocumentType.equals("Media") || subjectDocumentType.equals("Acquisition"))
156 && objectDocumentType.equals("CollectionObject")
158 String collectionObjectCsid = (String) doc.getProperty("relations_common", "objectCsid");
160 reindexCollectionObject(doc.getRepositoryName(), collectionObjectCsid);
163 else if (eventName.equals(DocumentEventTypes.DOCUMENT_REMOVED)) {
164 reindexPrevRelatedCollectionObjects(eventContext);
170 protected Logger getLogger() {
174 private void reindexMaterial(String repositoryName, String refName) {
175 if (StringUtils.isEmpty(refName) || !refName.startsWith(RefNameUtils.URN_PREFIX)) {
179 String escapedRefName = refName.replace("'", "\\'");
180 String query = String.format("SELECT ecm:uuid FROM Materialitem WHERE collectionspace_core:refName = '%s'", escapedRefName);
182 ElasticSearchComponent es = (ElasticSearchComponent) Framework.getService(ElasticSearchService.class);
183 es.runReindexingWorker(repositoryName, query);
186 private void reindexPrevRelatedCollectionObjects(DocumentEventContext eventContext) {
187 List<String> prevRelatedCollectionObjectCsids = (List<String>) eventContext.getProperty(PREV_RELATED_COLLECTION_OBJECT_CSID_KEY);
189 if (prevRelatedCollectionObjectCsids != null) {
190 for (String prevRelatedCollectionObjectCsid : prevRelatedCollectionObjectCsids) {
191 reindexCollectionObject(eventContext.getRepositoryName(), prevRelatedCollectionObjectCsid);
196 private void reindexRelatedCollectionObjects(DocumentModel doc) {
197 CoreSession session = doc.getCoreSession();
198 String repositoryName = doc.getRepositoryName();
199 String tenantId = (String) doc.getProperty("collectionspace_core", "tenantId");
200 String csid = doc.getName();
202 String relatedRecordQuery = String.format("SELECT * FROM Relation WHERE relations_common:subjectCsid = '%s' AND relations_common:objectDocumentType = 'CollectionObject' AND ecm:currentLifeCycleState = 'project' AND collectionspace_core:tenantId = '%s'", csid, tenantId);
203 DocumentModelList relationDocs = session.query(relatedRecordQuery);
204 List<String> collectionObjectCsids = new ArrayList<String>();
206 if (relationDocs.size() > 0) {
207 Iterator<DocumentModel> iterator = relationDocs.iterator();
209 while (iterator.hasNext()) {
210 DocumentModel relationDoc = iterator.next();
211 String collectionObjectCsid = (String) relationDoc.getProperty("relations_common", "objectCsid");
213 collectionObjectCsids.add(collectionObjectCsid);
217 for (String collectionObjectCsid : collectionObjectCsids) {
218 reindexCollectionObject(repositoryName, collectionObjectCsid);
222 private void reindexCollectionObject(String repositoryName, String csid) {
223 if (StringUtils.isEmpty(csid)) {
227 String query = String.format("SELECT ecm:uuid FROM CollectionObject WHERE ecm:name = '%s'", csid);
229 ElasticSearchComponent es = (ElasticSearchComponent) Framework.getService(ElasticSearchService.class);
230 es.runReindexingWorker(repositoryName, query);