/*
* 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.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package sh.isaac.api.index;
//~--- JDK imports ------------------------------------------------------------
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sh.isaac.api.Get;
import sh.isaac.api.LookupService;
import sh.isaac.api.component.sememe.SememeChronology;
import sh.isaac.api.component.sememe.version.SememeVersion;
import sh.isaac.api.task.TimedTask;
//~--- classes ----------------------------------------------------------------
/**
* The Class GenerateIndexes.
*
* @author kec
*/
public class GenerateIndexes
extends TimedTask<Void> {
/** The Constant log. */
private static final Logger log = LogManager.getLogger();
//~--- fields --------------------------------------------------------------
/** The processed. */
AtomicLong processed = new AtomicLong(0);
/** The indexers. */
List<IndexServiceBI> indexers;
/** The component count. */
long componentCount;
//~--- constructors --------------------------------------------------------
/**
* Instantiates a new generate indexes.
*
* @param indexersToReindex the indexers to reindex
*/
public GenerateIndexes(Class<?>... indexersToReindex) {
updateTitle("Index generation");
updateProgress(-1, Long.MAX_VALUE); // Indeterminate progress
if ((indexersToReindex == null) || (indexersToReindex.length == 0)) {
this.indexers = LookupService.get()
.getAllServices(IndexServiceBI.class);
} else {
this.indexers = new ArrayList<>();
for (final Class<?> clazz: indexersToReindex) {
if (!IndexServiceBI.class.isAssignableFrom(clazz)) {
throw new RuntimeException(
"Invalid Class passed in to the index generator. Classes must implement IndexService ");
}
final IndexServiceBI temp = (IndexServiceBI) LookupService.get()
.getService(clazz);
if (temp != null) {
this.indexers.add(temp);
}
}
}
final List<IndexStatusListenerBI> islList = LookupService.get()
.getAllServices(IndexStatusListenerBI.class);
for (final IndexServiceBI i: this.indexers) {
if (islList != null) {
for (final IndexStatusListenerBI isl: islList) {
isl.reindexBegan(i);
}
}
log.info("Clearing index for: " + i.getIndexerName());
i.clearIndex();
i.clearIndexedStatistics();
}
}
//~--- methods -------------------------------------------------------------
/**
* Call.
*
* @return the void
* @throws Exception the exception
*/
@Override
protected Void call()
throws Exception {
Get.activeTasks()
.add(this);
try {
// We only need to indexes sememes now
// In the future, there may be a need for indexing Concepts from the concept service - for instance, if we wanted to index the concepts
// by user, or by some other attribute that is attached to the concept. But there simply isn't much on the concept at present, and I have
// no use case for indexing the concepts. The IndexService APIs would need enhancement if we allowed indexing things other than sememes.
final long sememeCount = (int) Get.identifierService()
.getSememeSequenceStream()
.count();
log.info("Sememes to index: " + sememeCount);
this.componentCount = sememeCount;
for (final SememeChronology<?> sememe:
(Iterable<SememeChronology<? extends SememeVersion<?>>>) Get.sememeService()
.getParallelSememeStream()::iterator) {
for (final IndexServiceBI i: this.indexers) {
try {
if (sememe == null) {
// noop - this error is already logged elsewhere. Just skip.
} else {
i.index(sememe)
.get();
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
updateProcessedCount();
}
final List<IndexStatusListenerBI> islList = LookupService.get()
.getAllServices(IndexStatusListenerBI.class);
for (final IndexServiceBI i: this.indexers) {
if (islList != null) {
for (final IndexStatusListenerBI isl: islList) {
isl.reindexCompleted(i);
}
}
i.commitWriter();
i.forceMerge();
log.info(i.getIndexerName() + " indexing complete. Statistics follow:");
for (final Map.Entry<String, Integer> entry: i.reportIndexedItems()
.entrySet()) {
log.info(entry.getKey() + ": " + entry.getValue());
}
i.clearIndexedStatistics();
}
return null;
} finally {
Get.activeTasks()
.remove(this);
}
}
/**
* Update processed count.
*/
protected void updateProcessedCount() {
final long processedCount = this.processed.incrementAndGet();
if (processedCount % 1000 == 0) {
updateProgress(processedCount, this.componentCount);
updateMessage(String.format("Indexed %,d components...", processedCount));
// We were committing too often every 1000 components, it was bad for performance.
if (processedCount % 100000 == 0) {
for (final IndexServiceBI i: this.indexers) {
i.commitWriter();
}
}
}
}
}