/* * 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.Iterator; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.compass.core.CompassCallbackWithoutResult; import org.compass.core.CompassException; import org.compass.core.CompassSession; import org.compass.core.util.concurrent.NamedThreadFactory; import org.compass.gps.CompassGpsException; import org.compass.gps.spi.CompassGpsInterfaceDevice; /** * <p>Executes the indexing process using sevearl threads based on the partitioned * list of index entities. * * <p>By default (with <code>maxThreads</code> set to <code>-1</code>) creates N threads * during the indexing process where N is the number of partitioned index entities * groups (one thread per group). If <code>maxThreads<code> is set to a positive integer * number, the index executor will use it as the number of threads to create, regardless * of the number of partitioned entities groups. * * @author kimchy */ public class ConcurrentParallelIndexExecutor implements ParallelIndexExecutor { private static final Log log = LogFactory.getLog(ConcurrentParallelIndexExecutor.class); private int maxThreads = -1; /** * Constructs a new concurrent index executor with <code>maxThreads</code> * defaults to -1. */ public ConcurrentParallelIndexExecutor() { } /** * Constructs a new concurrent index executor with the given max threads. * * @param maxThreads The number of threads to use or -1 for dynamic threads */ public ConcurrentParallelIndexExecutor(int maxThreads) { if (maxThreads < -1 || maxThreads == 0) { throw new IllegalArgumentException("maxThreads must either be -1 or a value greater than 0"); } this.maxThreads = maxThreads; } /** * Performs the indexing process using the provided index entities indexer. Creates a pool of N * threads (if <code>maxThreads</code> is set to -1, N is the numer of entities groups, otherwise * N is the number of <code>maxThreads</code>). * * @param entities The partitioned index entities groups and index entities to index * @param indexEntitiesIndexer The entities indexer to use * @param compassGps Compass gps interface for meta information */ public void performIndex(final IndexEntity[][] entities, final IndexEntitiesIndexer indexEntitiesIndexer, final CompassGpsInterfaceDevice compassGps) { if (entities.length <= 0) { throw new IllegalArgumentException("No entities listed to be indexed, have you defined your entities correctly?"); } int maxThreads = this.maxThreads; if (maxThreads == -1) { maxThreads = entities.length; } ExecutorService executorService = Executors.newFixedThreadPool(maxThreads, new NamedThreadFactory("Compass Gps Index", false)); try { ArrayList tasks = new ArrayList(); for (int i = 0; i < entities.length; i++) { final IndexEntity[] indexEntities = entities[i]; tasks.add(new Callable() { public Object call() throws Exception { compassGps.executeForIndex(new CompassCallbackWithoutResult() { protected void doInCompassWithoutResult(CompassSession session) throws CompassException { indexEntitiesIndexer.performIndex(session, indexEntities); session.flush(); } }); return null; } }); } List futures; try { futures = executorService.invokeAll(tasks); } catch (InterruptedException e) { throw new CompassGpsException("Failed to index, interrupted", e); } for (Iterator it = futures.iterator(); it.hasNext();) { Future future = (Future) it.next(); try { future.get(); } catch (InterruptedException e) { throw new CompassGpsException("Failed to index, interrupted", e); } catch (ExecutionException e) { throw new CompassGpsException("Failed to index, execution exception", e); } } } finally { executorService.shutdownNow(); } } }