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

org.apache.sshd.common.future.AbstractSshFuture Maven / Gradle / Ivy

There is a newer version: 2.14.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF 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.apache.sshd.common.future;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.StreamCorruptedException;
import java.util.function.Function;

import org.apache.sshd.common.SshException;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;

/**
 * @param  Type of future
 * @author Apache MINA SSHD Project
 */
public abstract class AbstractSshFuture extends AbstractLoggingBean implements SshFuture {
    /**
     * A default value to indicate the future has been canceled
     */
    protected static final Object CANCELED = new Object();

    protected final boolean debugEnabled;
    protected final boolean traceEnabled;
    private final Object id;

    /**
     * @param id Some identifier useful as {@code toString()} value
     */
    protected AbstractSshFuture(Object id) {
        this.id = id;
        this.debugEnabled = log.isDebugEnabled();
        this.traceEnabled = log.isTraceEnabled();
    }

    @Override
    public Object getId() {
        return id;
    }

    @Override
    public boolean await(long timeoutMillis) throws IOException {
        return await0(timeoutMillis, true) != null;
    }

    @Override
    public boolean awaitUninterruptibly(long timeoutMillis) {
        try {
            return await0(timeoutMillis, false) != null;
        } catch (InterruptedIOException e) {
            throw formatExceptionMessage(msg -> new InternalError(msg, e),
                    "Unexpected interrupted exception wile awaitUninterruptibly %d msec: %s",
                    timeoutMillis, e.getMessage());
        }
    }

    /**
     * 

Waits (interruptible) for the specified timeout (msec.) and then checks * the result:

*
    *
  • * If result is {@code null} then timeout is assumed to have expired - throw * an appropriate {@link IOException} *

  • * *
  • * If the result is of the expected type, then cast and return it *

  • * *
  • * If the result is an {@link IOException} then re-throw it *

  • * *
  • * If the result is a {@link Throwable} then throw an {@link IOException} * whose cause is the original exception *

  • * *
  • * Otherwise (should never happen), throw a {@link StreamCorruptedException} * with the name of the result type *

  • *
* * @param The generic result type * @param expectedType The expected result type * @param timeout The timeout (millis) to wait for a result * @return The (never {@code null}) result * @throws IOException If failed to retrieve the expected result on time */ protected R verifyResult(Class expectedType, long timeout) throws IOException { Object value = await0(timeout, true); if (value == null) { throw formatExceptionMessage(SshException::new, "Failed to get operation result within specified timeout: %s", timeout); } Class actualType = value.getClass(); if (expectedType.isAssignableFrom(actualType)) { return expectedType.cast(value); } if (Throwable.class.isAssignableFrom(actualType)) { Throwable t = GenericUtils.peelException((Throwable) value); if (t != value) { value = t; actualType = value.getClass(); } if (IOException.class.isAssignableFrom(actualType)) { throw (IOException) value; } Throwable cause = GenericUtils.resolveExceptionCause(t); throw formatExceptionMessage(msg -> new SshException(msg, cause), "Failed (%s) to execute: %s", t.getClass().getSimpleName(), t.getMessage()); } else { // what else can it be ???? throw formatExceptionMessage(StreamCorruptedException::new, "Unknown result type: %s", actualType.getName()); } } /** * Wait for the Future to be ready. If the requested delay is 0 or * negative, this method returns immediately. * * @param timeoutMillis The delay we will wait for the Future to be ready * @param interruptable Tells if the wait can be interrupted or not. * If {@code true} and the thread is interrupted then an {@link InterruptedIOException} * is thrown. * @return The non-{@code null} result object if the Future is ready, * {@code null} if the timeout expired and no result was received * @throws InterruptedIOException If the thread has been interrupted when it's not allowed. */ protected abstract Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException; @SuppressWarnings("unchecked") protected SshFutureListener asListener(Object o) { return (SshFutureListener) o; } protected void notifyListener(SshFutureListener l) { try { l.operationComplete(asT()); } catch (Throwable t) { log.warn("notifyListener({}) failed ({}) to invoke {}: {}", this, t.getClass().getSimpleName(), l, t.getMessage()); if (debugEnabled) { log.debug("notifyListener(" + this + ")[" + l + "] invocation failure details", t); } } } @SuppressWarnings("unchecked") protected T asT() { return (T) this; } /** * Generates an exception whose message is prefixed by the future simple class name + {@link #getId() identifier} * as a hint to the context of the failure. * * @param Type of {@link Throwable} being generated * @param exceptionCreator The exception creator from the formatted message * @param format The extra payload format as per {@link String#format(String, Object...)} * @param args The formatting arguments * @return The generated exception */ protected E formatExceptionMessage(Function exceptionCreator, String format, Object... args) { String messagePayload = String.format(format, args); String excMessage = getClass().getSimpleName() + "[" + getId() + "]: " + messagePayload; return exceptionCreator.apply(excMessage); } @Override public String toString() { return getClass().getSimpleName() + "[id=" + getId() + "]"; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy