/*
* Copyright 2004-2009 the original author or authors.
*
* 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.
*/
package org.compass.gps.device.support.parallel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import org.compass.core.CompassSession;
import org.compass.core.spi.InternalCompass;
import org.compass.gps.CompassGpsException;
import org.compass.gps.IndexPlan;
import org.compass.gps.device.AbstractGpsDevice;
import org.compass.gps.spi.CompassGpsInterfaceDevice;
/**
* <p>A base class for gps device that can parallel the index operation.
*
* <p>When the device starts up, the {@link #doGetIndexEntities()} callback
* is called (should be implemented by sub classes) in order to get the
* {@link org.compass.gps.device.support.parallel.IndexEntity}ies. The
* {@link org.compass.gps.device.support.parallel.IndexEntitiesPartitioner} is
* then used in order to partition the index entities into several groups
* that can be parallel indexed. An {@link org.compass.gps.device.support.parallel.IndexEntitiesIndexer}
* is also obtained using the {@link #doGetIndexEntitiesIndexer()} that can
* index entities into the search engine and is provided by sub classes as well.
*
* <p>The {@link org.compass.gps.device.support.parallel.IndexEntitiesPartitioner}
* defaults to the {@link org.compass.gps.device.support.parallel.SubIndexIndexEntitiesPartitioner}
* that partition the index entities based on the sub index. This is the only meanigful
* way to partition the index entities, as it allows for the best concurrent support
* (locking is performed on the sub index level).
*
* <p>The {@link #index(org.compass.gps.IndexPlan)} operation uses the {@link org.compass.gps.device.support.parallel.ParallelIndexExecutor}
* in order to execute the indexing process. The default implementation used is
* {@link org.compass.gps.device.support.parallel.ConcurrentParallelIndexExecutor}.
*
* @author kimchy
*/
public abstract class AbstractParallelGpsDevice extends AbstractGpsDevice {
private ParallelIndexExecutor parallelIndexExecutor = new ConcurrentParallelIndexExecutor();
private IndexEntitiesPartitioner indexEntitiesPartitioner = new SubIndexIndexEntitiesPartitioner();
private volatile IndexEntity[][] entities;
private IndexEntitiesIndexer indexEntitiesIndexer;
/**
* Starts the device. Calls {@link #doGetIndexEntities} in order to get all the
* indexeable entities and uses the {@link org.compass.gps.device.support.parallel.IndexEntitiesPartitioner}
* to partition them index groups that can be parallel indexed. Also calls
* {@link #doGetIndexEntitiesIndexer()} in order to obtain the index entities indexer.
*
* @throws CompassGpsException
*/
public synchronized void start() throws CompassGpsException {
super.start();
indexEntitiesIndexer = doGetIndexEntitiesIndexer();
}
/**
* Index the indexable entities. Calls the {@link org.compass.gps.device.support.parallel.ParallelIndexExecutor}
* in order to index the different groups of indexed entities partitioned at startup.
*
* @throws CompassGpsException
*/
public synchronized void index(IndexPlan indexPlan) throws CompassGpsException {
if (!isRunning()) {
throw new IllegalStateException(
buildMessage("must be running in order to perform the index operation"));
}
entities = indexEntitiesPartitioner.partition(doGetIndexEntities());
// here, we go over and filter out things that are not based on the index plan
// we first get the sub indexes based on the index plan, and then filter out
// entities[] that do not match.
// Note: once we find a match, we must add all the entities array, since the partitioner
// "said" that they must be indexed 2ghter.
String[] arrSubIndexes = ((InternalCompass) ((CompassGpsInterfaceDevice) getGps()).getIndexCompass()).getSearchEngineFactory().getIndexManager().polyCalcSubIndexes(indexPlan.getSubIndexes(), indexPlan.getAliases(), indexPlan.getTypes());
HashSet<String> subIndexes = new HashSet<String>(Arrays.asList(arrSubIndexes));
ArrayList<IndexEntity[]> calcEntities = new ArrayList<IndexEntity[]>();
for (IndexEntity[] arrEleEntities : entities) {
boolean found = false;
for (IndexEntity entity : arrEleEntities) {
for (String subIndex : entity.getSubIndexes()) {
if (subIndexes.contains(subIndex)) {
found = true;
break;
}
}
if (found) {
break;
}
}
if (found) {
calcEntities.add(arrEleEntities);
}
}
IndexEntity[][] entitiesToIndex = calcEntities.toArray(new IndexEntity[calcEntities.size()][]);
parallelIndexExecutor.performIndex(entitiesToIndex, indexEntitiesIndexer, compassGps);
}
/**
* Returns all the indexed entities for this device.
*/
protected abstract IndexEntity[] doGetIndexEntities() throws CompassGpsException;
/**
* Returns an index entities indexer that knows how to index indexable entities.
*/
protected abstract IndexEntitiesIndexer doGetIndexEntitiesIndexer();
/**
* Overriding this method and throws an {@link IllegalStateException} as it should
* not be called. The {@link #index(org.compass.gps.IndexPlan)} operation is implemented here and does not
* call this method.
*/
protected final void doIndex(CompassSession session, IndexPlan indexPlan) throws CompassGpsException {
throw new IllegalStateException("This should not be called");
}
/**
* Sets the parallel index executor. Defaults to
* {@link org.compass.gps.device.support.parallel.ConcurrentParallelIndexExecutor}.
*
* @see #index(org.compass.gps.IndexPlan)
*/
public void setParallelIndexExecutor(ParallelIndexExecutor parallelIndexExecutor) {
this.parallelIndexExecutor = parallelIndexExecutor;
}
/**
* Sets the index entities partitioner. Defaults to
* {@link org.compass.gps.device.support.parallel.SubIndexIndexEntitiesPartitioner}.
*
* @see #start()
*/
public void setIndexEntitiesPartitioner(IndexEntitiesPartitioner indexEntitiesPartitioner) {
this.indexEntitiesPartitioner = indexEntitiesPartitioner;
}
}