/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.search.solr.internal.job; import java.util.Arrays; import javax.inject.Inject; import javax.inject.Named; import org.apache.commons.lang3.tuple.Pair; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; import org.xwiki.component.descriptor.ComponentInstantiationStrategy; import org.xwiki.job.AbstractJob; import org.xwiki.job.DefaultJobStatus; import org.xwiki.job.GroupedJob; import org.xwiki.job.JobGroupPath; import org.xwiki.job.Request; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.EntityReference; import org.xwiki.search.solr.internal.api.SolrIndexer; import org.xwiki.search.solr.internal.job.DiffDocumentIterator.Action; /** * Provide progress information and store logging of an advanced indexing. * * @version $Id: 1153d57d4e424c30169dd33eadf2dffbe0bb7eaa $ * @since 5.1RC1 */ @Component @InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP) @Named(IndexerJob.JOBTYPE) public class IndexerJob extends AbstractJob<IndexerRequest, DefaultJobStatus<IndexerRequest>> implements GroupedJob { /** * The id of the job. */ public static final String JOBTYPE = "solr.indexer"; /** * All indexers run in the same thread. */ // TODO: group indexers based on the IndexerRequest root entity private static final JobGroupPath GROUP = new JobGroupPath(Arrays.asList("solr", "indexer")); /** * Used to send documents to index or delete to/from Solr index. */ @Inject private transient SolrIndexer indexer; @Inject @Named("database") private transient DocumentIterator<String> databaseIterator; @Inject @Named("solr") private transient DocumentIterator<String> solrIterator; @Override public String getType() { return JOBTYPE; } @Override public JobGroupPath getGroupPath() { return GROUP; } @Override protected IndexerRequest castRequest(Request request) { IndexerRequest indexerRequest; if (request instanceof IndexerRequest) { indexerRequest = (IndexerRequest) request; } else { indexerRequest = new IndexerRequest(request); } return indexerRequest; } @Override protected void runInternal() throws Exception { if (getRequest().isOverwrite()) { EntityReference rootReference = getRequest().getRootReference(); this.logger.info("Index documents in [{}].", rootReference); this.indexer.index(rootReference, true); } else { updateSolrIndex(); } } /** * Update the Solr index to match the current state of the database. */ private void updateSolrIndex() { DiffDocumentIterator<String> iterator = new DiffDocumentIterator<>(this.solrIterator, this.databaseIterator); iterator.setRootReference(getRequest().getRootReference()); this.progressManager.pushLevelProgress(2, this); try { // Calculate index progress size this.progressManager.startStep(this); int progressSize = (int) iterator.size(); this.progressManager.endStep(this); // Index this.progressManager.startStep(this); updateSolrIndex(progressSize, iterator); this.progressManager.endStep(this); } finally { this.progressManager.popLevelProgress(this); } } private void updateSolrIndex(int progressSize, DiffDocumentIterator<String> iterator) { this.progressManager.pushLevelProgress(progressSize, this); try { long[] counter = new long[Action.values().length]; while (iterator.hasNext()) { this.progressManager.startStep(this); Pair<DocumentReference, Action> entry = iterator.next(); if (entry.getValue() == Action.ADD || entry.getValue() == Action.UPDATE) { // The database entry has not been indexed or the indexed version doesn't match the latest // version // from the database. this.indexer.index(entry.getKey(), true); } else if (entry.getValue() == Action.DELETE && getRequest().isRemoveMissing()) { // The index entry doesn't exist anymore in the database. this.indexer.delete(entry.getKey(), true); } counter[entry.getValue().ordinal()]++; this.progressManager.endStep(this); } this.logger.info( "{} documents added, {} deleted and {} updated during the synchronization of the Solr index.", counter[Action.ADD.ordinal()], counter[Action.DELETE.ordinal()], counter[Action.UPDATE.ordinal()]); } finally { this.progressManager.popLevelProgress(this); } } }