/* * Copyright (c) 2016 Fraunhofer IGD * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Fraunhofer IGD <http://www.igd.fraunhofer.de/> */ package de.fhg.igd.mapviewer.concurrency; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Job that allows handling partial results * * @param <T> the partial result type * @author Simon Templer */ public abstract class AbstractContinuousJob<T> extends Job<Void> { private boolean completed = false; private List<T> results = new ArrayList<T>(); private final Lock resultsLock = new ReentrantLock(); private List<T> processing = new ArrayList<T>(); private final Lock processingLock = new ReentrantLock(); private ProcessingStrategy strategy = new DefaultProcessingStrategy(10, 100); /** * Constructor * * @param name the job name */ public AbstractContinuousJob(String name) { super(name); } /** * @see Job#work(Progress) */ @Override final public Void work(Progress progress) throws Exception { try { doWork(progress); } catch (Throwable e) { error(e); } finally { completed = true; trigger(); } return null; } /** * Called when an error occurred while executing {@link #doWork(Progress)} * * @param e the error */ protected abstract void error(Throwable e); /** * Gets the work done, publish partial results using the * {@link #publish(Object)} method. * * @param progress the job progress * @throws Exception if an error occurs */ protected abstract void doWork(Progress progress) throws Exception; /** * Publish an object for processing * * @param object the object to publish */ protected void publish(T object) { resultsLock.lock(); try { results.add(object); } finally { resultsLock.unlock(); } trigger(); } /** * Trigger processing */ protected void trigger() { if (processingLock.tryLock()) { if (processing.isEmpty()) { // last processing is finished resultsLock.lock(); try { boolean allowed = strategy.allowProcess(results.size()); if (allowed || completed) { // check if processing is // allowed List<T> temp = processing; processing = results; results = temp; resultsLock.unlock(); if (!processing.isEmpty()) { // process collected results Thread thread = new Thread(new Runnable() { @Override public void run() { processingLock.lock(); try { process(new ArrayList<T>(processing)); } finally { processing.clear(); processingLock.unlock(); trigger(); } } }); thread.start(); } } else { resultsLock.unlock(); } } catch (Throwable e) { resultsLock.unlock(); } } processingLock.unlock(); } } /** * Process the results * * @param currentResults the list of current partial results */ protected abstract void process(List<T> currentResults); /** * @param strategy the strategy to set */ public void setStrategy(ProcessingStrategy strategy) { this.strategy = strategy; } }