001/* 002 * (C) Copyright 2014-2016 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 */ 020package org.nuxeo.elasticsearch.commands; 021 022import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BEFORE_DOC_UPDATE; 023import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.BINARYTEXT_UPDATED; 024import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDIN; 025import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHECKEDOUT; 026import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CHILDREN_ORDER_CHANGED; 027import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED; 028import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_CREATED_BY_COPY; 029import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_IMPORTED; 030import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_MOVED; 031import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_PROXY_UPDATED; 032import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_REMOVED; 033import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_SECURITY_UPDATED; 034import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.DOCUMENT_TAG_UPDATED; 035 036import java.util.Map; 037 038import org.apache.commons.logging.Log; 039import org.apache.commons.logging.LogFactory; 040import org.nuxeo.ecm.core.api.AbstractSession; 041import org.nuxeo.ecm.core.api.CoreSession; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.IdRef; 044import org.nuxeo.ecm.core.api.LifeCycleConstants; 045import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 046import org.nuxeo.elasticsearch.ElasticSearchConstants; 047import org.nuxeo.elasticsearch.commands.IndexingCommand.Type; 048import org.nuxeo.runtime.api.Framework; 049 050/** 051 * Contains logic to stack ElasticSearch commands depending on Document events This class is mainly here to make testing 052 * easier 053 */ 054public abstract class IndexingCommandsStacker { 055 056 protected static final Log log = LogFactory.getLog(IndexingCommandsStacker.class); 057 058 protected abstract Map<String, IndexingCommands> getAllCommands(); 059 060 protected abstract boolean isSyncIndexingByDefault(); 061 062 protected IndexingCommands getCommands(DocumentModel doc) { 063 return getAllCommands().get(getDocKey(doc)); 064 } 065 066 public void stackCommand(DocumentEventContext docCtx, String eventId) { 067 DocumentModel doc = docCtx.getSourceDocument(); 068 if (doc == null) { 069 return; 070 } 071 if ("/".equals(doc.getPathAsString())) { 072 log.debug("Skip indexing command for root document"); 073 return; 074 } 075 Boolean block = (Boolean) docCtx.getProperty(ElasticSearchConstants.DISABLE_AUTO_INDEXING); 076 if (block != null && block) { 077 if (log.isDebugEnabled()) { 078 log.debug("Indexing is disable, skip indexing command for doc " + doc); 079 } 080 return; 081 } 082 boolean sync = isSynchronous(docCtx, doc); 083 stackCommand(doc, eventId, sync); 084 } 085 086 protected boolean isSynchronous(DocumentEventContext docCtx, DocumentModel doc) { 087 // 1. look at event context 088 Boolean sync = (Boolean) docCtx.getProperty(ElasticSearchConstants.ES_SYNC_INDEXING_FLAG); 089 if (sync != null) { 090 return sync; 091 } 092 // 2. look at document context 093 sync = (Boolean) doc.getContextData(ElasticSearchConstants.ES_SYNC_INDEXING_FLAG); 094 if (sync != null) { 095 return sync; 096 } 097 // 3. get the default 098 sync = isSyncIndexingByDefault(); 099 return sync; 100 } 101 102 protected void stackCommand(DocumentModel doc, String eventId, boolean sync) { 103 IndexingCommands cmds = getOrCreateCommands(doc); 104 Type type; 105 boolean recurse = false; 106 switch (eventId) { 107 case DOCUMENT_CREATED: 108 case DOCUMENT_IMPORTED: 109 type = Type.INSERT; 110 break; 111 case DOCUMENT_CREATED_BY_COPY: 112 type = Type.INSERT; 113 recurse = isFolderish(doc); 114 break; 115 case BEFORE_DOC_UPDATE: 116 case DOCUMENT_CHECKEDOUT: 117 case BINARYTEXT_UPDATED: 118 case DOCUMENT_TAG_UPDATED: 119 case DOCUMENT_PROXY_UPDATED: 120 case LifeCycleConstants.TRANSITION_EVENT: 121 if (doc.isProxy() && !doc.isImmutable()) { 122 stackCommand(doc.getCoreSession().getDocument(new IdRef(doc.getSourceId())), BEFORE_DOC_UPDATE, false); 123 } 124 type = Type.UPDATE; 125 break; 126 case DOCUMENT_CHECKEDIN: 127 if (indexIsLatestVersion()) { 128 CoreSession session = doc.getCoreSession(); 129 if (session != null) { 130 // The previous doc version with isLastestVersion and isLatestMajorVersion need to be updated 131 // Here we have no way to get this exact doc version so we reindex all versions 132 for (DocumentModel version : doc.getCoreSession().getVersions(doc.getRef())) { 133 stackCommand(version, BEFORE_DOC_UPDATE, false); 134 } 135 } 136 } 137 type = Type.UPDATE; 138 break; 139 case DOCUMENT_MOVED: 140 type = Type.UPDATE; 141 recurse = isFolderish(doc); 142 break; 143 case DOCUMENT_REMOVED: 144 type = Type.DELETE; 145 recurse = isFolderish(doc); 146 break; 147 case DOCUMENT_SECURITY_UPDATED: 148 type = Type.UPDATE_SECURITY; 149 recurse = isFolderish(doc); 150 break; 151 case DOCUMENT_CHILDREN_ORDER_CHANGED: 152 type = Type.UPDATE_DIRECT_CHILDREN; 153 recurse = true; 154 break; 155 default: 156 return; 157 } 158 if (sync && recurse) { 159 // split into 2 commands one sync and an async recurse 160 cmds.add(type, true, false); 161 cmds.add(type, false, true); 162 } else { 163 cmds.add(type, sync, recurse); 164 } 165 } 166 167 private boolean indexIsLatestVersion() { 168 return !Framework.isBooleanPropertyTrue(AbstractSession.DISABLED_ISLATESTVERSION_PROPERTY); 169 } 170 171 private boolean isFolderish(DocumentModel doc) { 172 return doc.isFolder() && !doc.isVersion(); 173 } 174 175 protected IndexingCommands getOrCreateCommands(DocumentModel doc) { 176 IndexingCommands cmds = getCommands(doc); 177 if (cmds == null) { 178 cmds = new IndexingCommands(doc); 179 getAllCommands().put(getDocKey(doc), cmds); 180 } 181 return cmds; 182 } 183 184 protected String getDocKey(DocumentModel doc) { 185 // Don't merge commands with different session, so we work only on attached doc 186 return doc.getId() + "#" + doc.getSessionId(); 187 } 188 189}