/* * (C) Copyright 2014-2016 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Thierry Delprat * Benoit Delbosc */ package org.nuxeo.elasticsearch.commands; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BEFORE_DOC_UPDATE; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BINARYTEXT_UPDATED; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDIN; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDOUT; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHILDREN_ORDER_CHANGED; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED_BY_COPY; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_IMPORTED; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_MOVED; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_PROXY_UPDATED; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_REMOVED; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_SECURITY_UPDATED; import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_TAG_UPDATED; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.AbstractSession; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.IdRef; import org.nuxeo.ecm.core.api.LifeCycleConstants; import org.nuxeo.ecm.core.event.impl.DocumentEventContext; import org.nuxeo.elasticsearch.ElasticSearchConstants; import org.nuxeo.elasticsearch.commands.IndexingCommand.Type; import org.nuxeo.runtime.api.Framework; /** * Contains logic to stack ElasticSearch commands depending on Document events This class is mainly here to make testing * easier */ public abstract class IndexingCommandsStacker { protected static final Log log = LogFactory.getLog(IndexingCommandsStacker.class); protected abstract Map<String, IndexingCommands> getAllCommands(); protected abstract boolean isSyncIndexingByDefault(); protected IndexingCommands getCommands(DocumentModel doc) { return getAllCommands().get(getDocKey(doc)); } public void stackCommand(DocumentEventContext docCtx, String eventId) { DocumentModel doc = docCtx.getSourceDocument(); if (doc == null) { return; } if ("/".equals(doc.getPathAsString())) { log.debug("Skip indexing command for root document"); return; } Boolean block = (Boolean) docCtx.getProperty(ElasticSearchConstants.DISABLE_AUTO_INDEXING); if (block != null && block) { if (log.isDebugEnabled()) { log.debug("Indexing is disable, skip indexing command for doc " + doc); } return; } boolean sync = isSynchronous(docCtx, doc); stackCommand(doc, eventId, sync); } protected boolean isSynchronous(DocumentEventContext docCtx, DocumentModel doc) { // 1. look at event context Boolean sync = (Boolean) docCtx.getProperty(ElasticSearchConstants.ES_SYNC_INDEXING_FLAG); if (sync != null) { return sync; } // 2. look at document context sync = (Boolean) doc.getContextData(ElasticSearchConstants.ES_SYNC_INDEXING_FLAG); if (sync != null) { return sync; } // 3. get the default sync = isSyncIndexingByDefault(); return sync; } protected void stackCommand(DocumentModel doc, String eventId, boolean sync) { IndexingCommands cmds = getOrCreateCommands(doc); Type type; boolean recurse = false; switch (eventId) { case DOCUMENT_CREATED: case DOCUMENT_IMPORTED: type = Type.INSERT; break; case DOCUMENT_CREATED_BY_COPY: type = Type.INSERT; recurse = isFolderish(doc); break; case BEFORE_DOC_UPDATE: case DOCUMENT_CHECKEDOUT: case BINARYTEXT_UPDATED: case DOCUMENT_TAG_UPDATED: case DOCUMENT_PROXY_UPDATED: case LifeCycleConstants.TRANSITION_EVENT: if (doc.isProxy() && !doc.isImmutable()) { stackCommand(doc.getCoreSession().getDocument(new IdRef(doc.getSourceId())), BEFORE_DOC_UPDATE, false); } type = Type.UPDATE; break; case DOCUMENT_CHECKEDIN: if (indexIsLatestVersion()) { CoreSession session = doc.getCoreSession(); if (session != null) { // The previous doc version with isLastestVersion and isLatestMajorVersion need to be updated // Here we have no way to get this exact doc version so we reindex all versions for (DocumentModel version : doc.getCoreSession().getVersions(doc.getRef())) { stackCommand(version, BEFORE_DOC_UPDATE, false); } } } type = Type.UPDATE; break; case DOCUMENT_MOVED: type = Type.UPDATE; recurse = isFolderish(doc); break; case DOCUMENT_REMOVED: type = Type.DELETE; recurse = isFolderish(doc); break; case DOCUMENT_SECURITY_UPDATED: type = Type.UPDATE_SECURITY; recurse = isFolderish(doc); break; case DOCUMENT_CHILDREN_ORDER_CHANGED: type = Type.UPDATE_DIRECT_CHILDREN; recurse = true; break; default: return; } if (sync && recurse) { // split into 2 commands one sync and an async recurse cmds.add(type, true, false); cmds.add(type, false, true); } else { cmds.add(type, sync, recurse); } } private boolean indexIsLatestVersion() { return !Framework.isBooleanPropertyTrue(AbstractSession.DISABLED_ISLATESTVERSION_PROPERTY); } private boolean isFolderish(DocumentModel doc) { return doc.isFolder() && !doc.isVersion(); } protected IndexingCommands getOrCreateCommands(DocumentModel doc) { IndexingCommands cmds = getCommands(doc); if (cmds == null) { cmds = new IndexingCommands(doc); getAllCommands().put(getDocKey(doc), cmds); } return cmds; } protected String getDocKey(DocumentModel doc) { // Don't merge commands with different session, so we work only on attached doc return doc.getId() + "#" + doc.getSessionId(); } }