001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Thierry Delprat
018 *     Benoit Delbosc
019 */
020
021package org.nuxeo.elasticsearch.commands;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.nuxeo.ecm.core.api.DocumentModel;
026import org.nuxeo.ecm.core.api.LifeCycleConstants;
027import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
028import org.nuxeo.elasticsearch.ElasticSearchConstants;
029import org.nuxeo.elasticsearch.commands.IndexingCommand.Type;
030import java.util.Map;
031
032import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BEFORE_DOC_UPDATE;
033import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BINARYTEXT_UPDATED;
034import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDIN;
035import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDOUT;
036import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHILDREN_ORDER_CHANGED;
037import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED;
038import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED_BY_COPY;
039import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_MOVED;
040import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_PROXY_UPDATED;
041import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_REMOVED;
042import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_SECURITY_UPDATED;
043import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_TAG_UPDATED;
044
045/**
046 * Contains logic to stack ElasticSearch commands depending on Document events This class is mainly here to make testing
047 * easier
048 */
049public abstract class IndexingCommandsStacker {
050
051    protected static final Log log = LogFactory.getLog(IndexingCommandsStacker.class);
052
053    protected abstract Map<String, IndexingCommands> getAllCommands();
054
055    protected abstract boolean isSyncIndexingByDefault();
056
057    protected IndexingCommands getCommands(DocumentModel doc) {
058        return getAllCommands().get(getDocKey(doc));
059    }
060
061    public void stackCommand(DocumentEventContext docCtx, String eventId) {
062        DocumentModel doc = docCtx.getSourceDocument();
063        if (doc == null) {
064            return;
065        }
066        Boolean block = (Boolean) docCtx.getProperty(ElasticSearchConstants.DISABLE_AUTO_INDEXING);
067        if (block != null && block) {
068            if (log.isDebugEnabled()) {
069                log.debug("Indexing is disable, skip indexing command for doc " + doc);
070            }
071            return;
072        }
073        boolean sync = isSynchronous(docCtx, doc);
074        stackCommand(doc, eventId, sync);
075    }
076
077    protected boolean isSynchronous(DocumentEventContext docCtx, DocumentModel doc) {
078        // 1. look at event context
079        Boolean sync = (Boolean) docCtx.getProperty(ElasticSearchConstants.ES_SYNC_INDEXING_FLAG);
080        if (sync != null) {
081            return sync;
082        }
083        // 2. look at document context
084        sync = (Boolean) doc.getContextData().get(ElasticSearchConstants.ES_SYNC_INDEXING_FLAG);
085        if (sync != null) {
086            return sync;
087        }
088        // 3. get the default
089        sync = isSyncIndexingByDefault();
090        return sync;
091    }
092
093    protected void stackCommand(DocumentModel doc, String eventId, boolean sync) {
094        IndexingCommands cmds = getOrCreateCommands(doc);
095        Type type;
096        boolean recurse = false;
097        switch (eventId) {
098            case DOCUMENT_CREATED:
099            case LifeCycleConstants.TRANSITION_EVENT:
100                type = Type.INSERT;
101                break;
102            case DOCUMENT_CREATED_BY_COPY:
103                type = Type.INSERT;
104                recurse = isFolderish(doc);
105                break;
106            case BEFORE_DOC_UPDATE:
107            case DOCUMENT_CHECKEDOUT:
108            case DOCUMENT_CHECKEDIN:
109            case BINARYTEXT_UPDATED:
110            case DOCUMENT_TAG_UPDATED:
111            case DOCUMENT_PROXY_UPDATED:
112                type = Type.UPDATE;
113                break;
114            case DOCUMENT_MOVED:
115                type = Type.UPDATE;
116                recurse = isFolderish(doc);
117                break;
118            case DOCUMENT_REMOVED:
119                type = Type.DELETE;
120                recurse = isFolderish(doc);
121                break;
122            case DOCUMENT_SECURITY_UPDATED:
123                type = Type.UPDATE_SECURITY;
124                recurse = isFolderish(doc);
125                break;
126            case DOCUMENT_CHILDREN_ORDER_CHANGED:
127                type = Type.UPDATE_DIRECT_CHILDREN;
128                recurse = true;
129                break;
130            default:
131                return;
132        }
133        if (sync && recurse) {
134            // split into 2 commands one sync and an async recurse
135            cmds.add(type, true, false);
136            cmds.add(type, false, true);
137        } else {
138            cmds.add(type, sync, recurse);
139        }
140    }
141
142    private boolean isFolderish(DocumentModel doc) {
143        return doc.isFolder() && ! doc.isVersion();
144    }
145
146    protected IndexingCommands getOrCreateCommands(DocumentModel doc) {
147        IndexingCommands cmds = getCommands(doc);
148        if (cmds == null) {
149            cmds = new IndexingCommands(doc);
150            getAllCommands().put(getDocKey(doc), cmds);
151        }
152        return cmds;
153    }
154
155    protected String getDocKey(DocumentModel doc) {
156        // Don't merge commands with different session, so we work only on attached doc
157        return doc.getId() + "#" + doc.getSessionId();
158    }
159
160}