All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.arquillian.spacelift.execution.impl.FutureBasedExecution Maven / Gradle / Ivy

There is a newer version: 1.0.2
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2014, Red Hat Middleware LLC, and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * 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.arquillian.spacelift.execution.impl;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.arquillian.spacelift.execution.CountDownWatch;
import org.arquillian.spacelift.execution.Execution;
import org.arquillian.spacelift.execution.ExecutionCondition;
import org.arquillian.spacelift.execution.ExecutionException;
import org.arquillian.spacelift.execution.ExecutionService;
import org.arquillian.spacelift.execution.TimeoutExecutionException;

/**
 * Execution that is based on {@see Future} and causes thread to block if await is called.
 *
 * @param 
 *     Deferred result of the execution
 *
 * @author Karel Piwko
 */
class FutureBasedExecution implements Execution {

    public static final long DEFAULT_POLL_INTERVAL = 500;
    public static final TimeUnit DEFAULT_POLL_TIME_UNIT = TimeUnit.MILLISECONDS;

    private final Callable executionTask;
    private final Future executionFuture;
    private final ExecutionService service;

    private long pollInterval;
    private TimeUnit pollUnit;

    private boolean shouldBeFinished;

    public FutureBasedExecution(ExecutionService service, Callable task, Future future) {
        this.service = service;
        this.executionTask = task;
        this.executionFuture = future;
        this.pollInterval = DEFAULT_POLL_INTERVAL;
        this.pollUnit = DEFAULT_POLL_TIME_UNIT;
    }

    private static ExecutionException unwrapException(Throwable cause, String messageFormat, Object... parameters) {
        Throwable current = cause;
        ExecutionException deepestCause = null;
        while (current != null) {
            if (current instanceof ExecutionException) {
                deepestCause = (ExecutionException) current;
            }
            current = current.getCause();
        }

        if (deepestCause != null) {
            return deepestCause.prependMessage(messageFormat, parameters);
        }

        return new ExecutionException(cause, messageFormat, parameters);
    }

    private static TimeoutExecutionException unwrapExceptionAsTimeoutException(Throwable cause, String messageFormat,
        Object... parameters) {
        Throwable current = cause;
        while (current != null) {
            if (current instanceof ExecutionException) {
                return new TimeoutExecutionException(current, messageFormat, parameters);
            }
            current = current.getCause();
        }

        return new TimeoutExecutionException(cause, messageFormat, parameters);
    }

    @Override
    public Execution markAsFinished() {
        this.shouldBeFinished = true;
        return this;
    }

    @Override
    public Execution registerShutdownHook() {
        ShutdownHooks.addHookFor(this);
        return this;
    }

    @Override
    public boolean isMarkedAsFinished() {
        return shouldBeFinished;
    }

    @Override
    public boolean isFinished() {
        return isMarkedAsFinished() || executionFuture.isDone();
    }

    @Override
    public boolean hasFailed() {
        return executionFuture.isCancelled();
    }

    @Override
    public Execution terminate() {
        executionFuture.cancel(true);
        return this;
    }

    @Override
    public RESULT await() throws ExecutionException {
        try {
            return executionFuture.get();
        } catch (InterruptedException e) {
            throw unwrapException(e, "Interrupted while executing a task");
        } catch (java.util.concurrent.ExecutionException e) {
            throw unwrapException(e, "Execution of a task failed");
        }
    }

    @Override
    public RESULT awaitAtMost(long timeout, TimeUnit unit) {
        try {
            return executionFuture.get(timeout, unit);
        } catch (InterruptedException e) {
            throw unwrapException(e, "Interrupted while executing a task");
        } catch (java.util.concurrent.ExecutionException e) {
            throw unwrapException(e, "Execution of a task failed");
        } catch (TimeoutException e) {
            throw unwrapExceptionAsTimeoutException(e, "Timed out after {0}{1} while executing a task", timeout, unit);
        }
    }

    @Override
    public Execution reexecuteEvery(long step, TimeUnit unit) {
        this.pollInterval = step;
        this.pollUnit = unit;
        return this;
    }

    @Override
    public RESULT until(long timeout, TimeUnit unit, ExecutionCondition condition) throws ExecutionException,
        TimeoutExecutionException {

        CountDownWatch countdown = new CountDownWatch(timeout, unit);
        Execution currentExecution = new FutureBasedExecution(service, executionTask, executionFuture);

        // keep scheduling task until we have some time
        while (countdown.timeLeft() > 0) {

            Execution nextExecution = service.schedule(executionTask, pollInterval, pollUnit);

            try {
                RESULT result = currentExecution.awaitAtMost(countdown.timeLeft(), countdown.getTimeUnit());
                if (condition.satisfiedBy(result)) {
                    // terminate execution of next callable
                    // we want to ignore failures in termination
                    try {
                        nextExecution.terminate();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }
                    return result;
                }
            } catch (TimeoutExecutionException e) {
                continue;
            }

            // continue evaluating scheduled execution
            currentExecution = nextExecution;
        }

        throw new TimeoutExecutionException("Unable to trigger condition within {0} {1}.", timeout, unit.toString()
            .toLowerCase());
    }

    @Override
    public RESULT awaitAtMost(CountDownWatch timeout) throws ExecutionException, TimeoutExecutionException {
        return awaitAtMost(timeout.timeout(), timeout.getTimeUnit());
    }

    @Override
    public RESULT until(CountDownWatch timeout, ExecutionCondition condition)
        throws ExecutionException, TimeoutExecutionException {
        return until(timeout.timeout(), timeout.getTimeUnit(), condition);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy