org.makersoft.shards.strategy.access.impl.ParallelShardOperationCallable.java Source code

Java tutorial

Introduction

Here is the source code for org.makersoft.shards.strategy.access.impl.ParallelShardOperationCallable.java

Source

/*
 * @(#)ParallelShardOperationCallable.java 2012-8-1 ?10:00:00
 *
 * Copyright (c) 2011-2012 Makersoft.org all rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *
 */
package org.makersoft.shards.strategy.access.impl;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.makersoft.shards.Shard;
import org.makersoft.shards.ShardId;
import org.makersoft.shards.ShardOperation;
import org.makersoft.shards.strategy.exit.ExitStrategy;

/**
 * Runs a single operation on a single shard, collecting the result of the
 * operation with an ExitStrategy.  The interesting bit here is that
 * if the ExitStrategy indicates that there is no more work to be performed,
 * this object has the ability to cancel the work being performed by all the
 * other threads.
 *
 */
class ParallelShardOperationCallable<T> implements Callable<Void> {

    private static final boolean INTERRUPT_IF_RUNNING = false;

    private final Log log = LogFactory.getLog(getClass());

    private final CountDownLatch startSignal;

    private final CountDownLatch doneSignal;

    private final ExitStrategy<T> exitStrategy;

    private final ShardOperation<T> operation;

    private final Shard shard;

    private final ShardId shardId;

    private final List<StartAwareFutureTask> futureTasks;

    public ParallelShardOperationCallable(CountDownLatch startSignal, CountDownLatch doneSignal,
            ExitStrategy<T> exitStrategy, ShardOperation<T> operation, Shard shard, ShardId shardId,
            List<StartAwareFutureTask> futureTasks) {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
        this.exitStrategy = exitStrategy;
        this.operation = operation;
        this.shard = shard;
        this.shardId = shardId;
        this.futureTasks = futureTasks;
    }

    public Void call() throws Exception {
        try {
            waitForStartSignal();
            log.debug(String.format("Starting execution of %s against shard %s", operation.getOperationName(),
                    shard));
            /**
             * If addResult() returns true it means there is no more work to be
             * performed.  Cancel all the outstanding tasks.
             */
            if (exitStrategy.addResult(operation.execute(shard.establishSqlSession(), shardId), shard)) {
                log.debug(String.format(
                        "Short-circuiting execution of %s on other threads after execution against shard %s",
                        operation.getOperationName(), shard));
                /**
                 * It's ok to cancel ourselves because StartAwareFutureTask.cancel()
                 * will return false if a task has already started executing, and we're
                 * already executing.
                 */

                log.debug(String.format("Checking %d future tasks to see if they need to be cancelled.",
                        futureTasks.size()));
                for (StartAwareFutureTask ft : futureTasks) {
                    log.debug(String.format("Preparing to cancel future task %d.", ft.getId()));
                    /**
                     * If a task was successfully cancelled that means it had not yet
                     * started running.  Since the task won't run, the task won't be
                     * able to decrement the CountDownLatch.  We need to decrement
                     * it on behalf of the cancelled task.
                     */
                    if (ft.cancel(INTERRUPT_IF_RUNNING)) {
                        log.debug("Task cancel returned true, decrementing counter on its behalf.");
                        doneSignal.countDown();
                    } else {
                        log.debug("Task cancel returned false, not decrementing counter on its behalf.");
                    }
                }
            } else {
                log.debug(String.format(
                        "No need to short-cirtcuit execution of %s on other threads after execution against shard %s",
                        operation.getOperationName(), shard));
            }
        } finally {
            // counter must get decremented no matter what
            log.debug(String.format("Decrementing counter for operation %s on shard %s",
                    operation.getOperationName(), shard));
            doneSignal.countDown();
        }
        return null;
    }

    private void waitForStartSignal() {
        try {
            startSignal.await();
        } catch (InterruptedException e) {
            // I see no reason why this should happen
            final String msg = String.format(
                    "Received interrupt while waiting to begin execution of %s against shard %s",
                    operation.getOperationName(), shard);
            log.error(msg);
            throw new RuntimeException(msg);
        }
    }
}