/*
* StreamCruncher: Copyright (c) 2006-2008, Ashwin Jayaprakash. All Rights Reserved.
* Contact: ashwin {dot} jayaprakash {at} gmail {dot} com
* Web: http://www.StreamCruncher.com
*
* This file is part of StreamCruncher.
*
* StreamCruncher 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 3 of the License, or
* (at your option) any later version.
*
* StreamCruncher 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 StreamCruncher. If not, see <http://www.gnu.org/licenses/>.
*/
package streamcruncher.util;
import java.util.IdentityHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import streamcruncher.boot.Registry;
import streamcruncher.kernel.JobExecutionManager;
import streamcruncher.kernel.QueryRunnerJob;
import streamcruncher.util.sysevent.SystemEvent;
import streamcruncher.util.sysevent.SystemEventBus;
import streamcruncher.util.sysevent.SystemEvent.Priority;
/*
* Author: Ashwin Jayaprakash Date: Apr 9, 2006 Time: 5:04:03 PM
*/
public class SimpleJobFixedBatchExecutor {
public static final int STUCK_JOB_CHECK_DIVISOR = 2;
protected final JobExecutionManager executionManager;
protected final SimpleJob[] jobs;
protected final String name;
protected final IdentityHashMap<Future, SimpleJob> futures;
protected final ReusableCountDownLatch countDownLatch;
protected final Logger logger;
/**
* Must be set before invoking {@link #runJobs()}, if it has to reflect in
* that cycle.
*/
protected long stuckJobInterruptionTimeMsecs = 20 * 1000;
public SimpleJobFixedBatchExecutor(SimpleJob[] jobs, String name,
ReusableCountDownLatch countDownLatch) {
this.executionManager = Registry.getImplFor(JobExecutionManager.class);
this.jobs = jobs;
this.name = name;
this.futures = new IdentityHashMap<Future, SimpleJob>();
this.countDownLatch = countDownLatch;
this.logger = Registry.getImplFor(LoggerManager.class).getLogger(
SimpleJobFixedBatchExecutor.class.getName());
}
/**
* @return Returns the jobs.
*/
public SimpleJob[] getJobs() {
return jobs;
}
public String getName() {
return name;
}
public long getStuckJobInterruptionTimeMsecs() {
return stuckJobInterruptionTimeMsecs;
}
public void setStuckJobInterruptionTimeMsecs(long stuckJobInterruptionTimeMsecs) {
this.stuckJobInterruptionTimeMsecs = stuckJobInterruptionTimeMsecs;
}
// -------------------
/**
* This method will return only <b>after</b> all the Jobs have completed
* (successfully or with Exceptions).
*
* @throws SimpleJobBatchExecutionException
*/
public void runJobs() throws SimpleJobBatchExecutionException {
countDownLatch.startNewCycle(jobs.length);
futures.clear();
try {
for (SimpleJob job : jobs) {
Future future = executionManager.submitJob(job);
futures.put(future, job);
}
}
finally {
try {
handleLatchAwait();
}
catch (InterruptedException e) {
throw new SimpleJobBatchExecutionException(e);
}
}
// ---------------
SimpleJobBatchExecutionException batchExecutionException = null;
for (SimpleJob job : jobs) {
Throwable throwable = job.getErrorInRun();
if (throwable != null) {
if (batchExecutionException == null) {
batchExecutionException = new SimpleJobBatchExecutionException(
"Error(s) occurred while executing the Jobs.");
}
batchExecutionException.addError(job.getName(), throwable);
// -----------
SystemEventBus bus = Registry.getImplFor(SystemEventBus.class);
SystemEvent event = new SystemEvent(QueryRunnerJob.class.getName(), getName() + "."
+ job.getName(), throwable, Priority.SEVERE);
bus.submit(event);
}
}
if (batchExecutionException != null) {
throw batchExecutionException;
}
}
protected void handleLatchAwait() throws InterruptedException {
final long recheckTimeMsecs = stuckJobInterruptionTimeMsecs / STUCK_JOB_CHECK_DIVISOR;
int count = 0;
while (countDownLatch.await(recheckTimeMsecs, TimeUnit.MILLISECONDS) > 0) {
count++;
long time = (count * recheckTimeMsecs);
String msg = "Jobs in Batch: " + getName() + " are still running even after: " + time
+ " milliseconds.";
int notDone = 0;
for (Future future : futures.keySet()) {
SimpleJob job = futures.get(future);
msg = msg + " [" + job.getName() + " Completed: " + future.isDone() + "]";
if (future.isDone() == false) {
notDone++;
}
}
Priority eventPriority = Priority.WARNING;
if (count >= STUCK_JOB_CHECK_DIVISOR) {
logger.log(Level.SEVERE, msg);
msg = "Interrupting Jobs that are still running in Batch: " + getName();
for (Future future : futures.keySet()) {
if (future.isDone() == false) {
boolean cancelled = future.cancel(true);
SimpleJob job = futures.get(future);
msg = msg + " [" + job.getName() + " Cancelled: " + cancelled + "]";
}
}
logger.log(Level.SEVERE, "Interrupting Jobs that are still running in Batch: "
+ getName());
eventPriority = Priority.SEVERE;
}
else {
logger.log(Level.WARNING, msg);
}
SystemEventBus bus = Registry.getImplFor(SystemEventBus.class);
SystemEvent event = new SystemEvent(QueryRunnerJob.class.getName(), getName(), msg,
eventPriority);
bus.submit(event);
}
}
}