org.jclouds.rest.internal.BlockOnFuture.java Source code

Java tutorial

Introduction

Here is the source code for org.jclouds.rest.internal.BlockOnFuture.java

Source

/**
 * Licensed to jclouds, Inc. (jclouds) under one or more
 * contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  jclouds licenses this file
 * to you 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.jclouds.rest.internal;

import static com.google.common.base.Optional.fromNullable;
import static com.google.common.collect.ObjectArrays.concat;
import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;

import org.jclouds.logging.Logger;
import org.jclouds.reflect.Invocation;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.reflect.Invokable;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.common.util.concurrent.UncheckedTimeoutException;
import com.google.inject.assistedinject.Assisted;

public class BlockOnFuture implements Function<ListenableFuture<?>, Object> {

    public static interface Factory {
        /**
         * @param invocation
         *           context for how the future was created
         */
        BlockOnFuture create(Invocation invocation);
    }

    @Resource
    private Logger logger = Logger.NULL;

    private final Map<String, Long> timeouts;
    private final Invocation invocation;

    @Inject
    @VisibleForTesting
    BlockOnFuture(@Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted Invocation invocation) {
        this.timeouts = timeouts;
        this.invocation = invocation;
    }

    @Override
    public Object apply(ListenableFuture<?> future) {
        Optional<Long> timeoutNanos = timeoutInNanos(invocation.getInvokable(), timeouts);
        return block(future, timeoutNanos);
    }

    private Object block(ListenableFuture<?> future, Optional<Long> timeoutNanos) {
        try {
            if (timeoutNanos.isPresent()) {
                logger.debug(">> blocking on %s for %s", future, timeoutNanos);
                return getUninterruptibly(future, timeoutNanos.get(), NANOSECONDS);
            } else {
                logger.debug(">> blocking on %s", future);
                return getUninterruptibly(future);
            }
        } catch (ExecutionException e) {
            throw propagateCause(e);
        } catch (TimeoutException e) {
            future.cancel(true);
            throw new UncheckedTimeoutException(e);
        }
    }

    private static RuntimeException propagateCause(Exception e) {
        Throwable cause = e.getCause();
        if (cause == null) {
            UncheckedExecutionException unchecked = new UncheckedExecutionException(e.getMessage()) {
                private static final long serialVersionUID = 1L;
            };
            unchecked.setStackTrace(e.getStackTrace());
            throw unchecked;
        }
        StackTraceElement[] combined = concat(cause.getStackTrace(), e.getStackTrace(), StackTraceElement.class);
        cause.setStackTrace(combined);
        if (cause instanceof RuntimeException) {
            throw (RuntimeException) cause;
        }
        if (cause instanceof Error) {
            throw (Error) cause;
        }
        // The cause is a weird kind of Throwable, so throw the outer exception.
        throw new RuntimeException(e);
    }

    // override timeout by values configured in properties(in ms)
    private Optional<Long> timeoutInNanos(Invokable<?, ?> invoked, Map<String, Long> timeouts) {
        Optional<Long> defaultMillis = fromNullable(timeouts.get("default"));
        Optional<Long> timeoutMillis;
        if (invoked.isAnnotationPresent(Named.class)) {
            String commandName = invoked.getAnnotation(Named.class).value();
            timeoutMillis = fromNullable(timeouts.get(commandName)).or(defaultMillis);
        } else {
            // TODO: remove old logic, once Named annotations are present on all methods
            String className = invoked.getOwnerType().getRawType().getSimpleName().replace("AsyncClient", "Client")
                    .replace("AsyncApi", "Api");
            timeoutMillis = fromNullable(timeouts.get(className + "." + invoked.getName()))
                    .or(fromNullable(timeouts.get(className))).or(defaultMillis);
        }
        if (timeoutMillis.isPresent())
            return Optional.of(TimeUnit.MILLISECONDS.toNanos(timeoutMillis.get()));
        return Optional.absent();
    }
}