001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Thierry Delprat
016 *     Benoit Delbosc
017 */
018
019package org.nuxeo.elasticsearch.commands;
020
021import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BEFORE_DOC_UPDATE;
022import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BINARYTEXT_UPDATED;
023import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDIN;
024import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDOUT;
025import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED;
026import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED_BY_COPY;
027import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_MOVED;
028import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_PROXY_UPDATED;
029import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_REMOVED;
030import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_SECURITY_UPDATED;
031import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_TAG_UPDATED;
032
033import java.util.Map;
034
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.nuxeo.ecm.core.api.DocumentModel;
038import org.nuxeo.ecm.core.api.LifeCycleConstants;
039import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
040import org.nuxeo.elasticsearch.ElasticSearchConstants;
041import org.nuxeo.elasticsearch.commands.IndexingCommand.Type;
042import org.nuxeo.runtime.api.Framework;
043
044/**
045 * Contains logic to stack ElasticSearch commands depending on Document events This class is mainly here to make testing
046 * easier
047 */
048public abstract class IndexingCommandsStacker {
049
050    protected static final Log log = LogFactory.getLog(IndexingCommandsStacker.class);
051
052    protected abstract Map<String, IndexingCommands> getAllCommands();
053
054    protected abstract boolean isSyncIndexingByDefault();
055
056    protected IndexingCommands getCommands(DocumentModel doc) {
057        return getAllCommands().get(getDocKey(doc));
058    }
059
060    public void stackCommand(DocumentEventContext docCtx, String eventId) {
061        if (Framework.isTestModeSet() && Framework.getRuntime().isShuttingDown()) {
062            // avoid processing events generated by the shutdown process in test framework
063            return;
064        }
065        DocumentModel doc = docCtx.getSourceDocument();
066        if (doc == null) {
067            return;
068        }
069        Boolean block = (Boolean) docCtx.getProperty(ElasticSearchConstants.DISABLE_AUTO_INDEXING);
070        if (block != null && block) {
071            if (log.isDebugEnabled()) {
072                log.debug("Indexing is disable, skip indexing command for doc " + doc);
073            }
074            return;
075        }
076        boolean sync = isSynchronous(docCtx, doc);
077        stackCommand(doc, eventId, sync);
078    }
079
080    protected boolean isSynchronous(DocumentEventContext docCtx, DocumentModel doc) {
081        // 1. look at event context
082        Boolean sync = (Boolean) docCtx.getProperty(ElasticSearchConstants.ES_SYNC_INDEXING_FLAG);
083        if (sync != null) {
084            return sync;
085        }
086        // 2. look at document context
087        sync = (Boolean) doc.getContextData().get(ElasticSearchConstants.ES_SYNC_INDEXING_FLAG);
088        if (sync != null) {
089            return sync;
090        }
091        // 3. get the default
092        sync = isSyncIndexingByDefault();
093        return sync;
094    }
095
096    protected void stackCommand(DocumentModel doc, String eventId, boolean sync) {
097        IndexingCommands cmds = getOrCreateCommands(doc);
098        Type type;
099        boolean recurse = false;
100        switch (eventId) {
101        case DOCUMENT_CREATED:
102        case LifeCycleConstants.TRANSITION_EVENT:
103            type = Type.INSERT;
104            break;
105        case DOCUMENT_CREATED_BY_COPY:
106            type = Type.INSERT;
107            recurse = doc.isFolder();
108            break;
109        case BEFORE_DOC_UPDATE:
110        case DOCUMENT_CHECKEDOUT:
111        case DOCUMENT_CHECKEDIN:
112        case BINARYTEXT_UPDATED:
113        case DOCUMENT_TAG_UPDATED:
114        case DOCUMENT_PROXY_UPDATED:
115            type = Type.UPDATE;
116            break;
117        case DOCUMENT_MOVED:
118            type = Type.UPDATE;
119            recurse = doc.isFolder();
120            break;
121        case DOCUMENT_REMOVED:
122            type = Type.DELETE;
123            recurse = doc.isFolder();
124            break;
125        case DOCUMENT_SECURITY_UPDATED:
126            type = Type.UPDATE_SECURITY;
127            recurse = doc.isFolder();
128            break;
129        default:
130            return;
131        }
132        if (sync && recurse) {
133            // split into 2 commands one sync and an async recurse
134            cmds.add(type, true, false);
135            cmds.add(type, false, true);
136        } else {
137            cmds.add(type, sync, recurse);
138        }
139    }
140
141    protected IndexingCommands getOrCreateCommands(DocumentModel doc) {
142        IndexingCommands cmds = getCommands(doc);
143        if (cmds == null) {
144            cmds = new IndexingCommands(doc);
145            getAllCommands().put(getDocKey(doc), cmds);
146        }
147        return cmds;
148    }
149
150    protected String getDocKey(DocumentModel doc) {
151        // Don't merge commands with different session, so we work only on attached doc
152        return doc.getId() + "#" + doc.getSessionId();
153    }
154
155}