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.net.ConnectException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;

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

/**
 * @param   Type of future
 * @author     Apache MINA SSHD Project
 */
public abstract class AbstractSshFuture> extends AbstractLoggingBean implements SshFuture {

    private final Object id;

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

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

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

    @Override
    public boolean awaitUninterruptibly(long timeoutMillis, CancelOption... options) {
        try {
            return await0(timeoutMillis, false, options) != null;
        } catch (InterruptedIOException e) {
            throw formatExceptionMessage(
                    msg -> new IllegalStateException(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 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 * @param options Optional {@link CancelOption}s defining the behavior on time-out or interrupt. * @return The (never {@code null}) result * @throws IOException If failed to retrieve the expected result on time */ protected R verifyResult(Class expectedType, long timeout, CancelOption... options) throws IOException { Object value = await0(timeout, true, options); if (value == null) { TimeoutException cause = new TimeoutException("Timed out after " + timeout + " msec"); throw formatExceptionMessage(msg -> new SshException(msg, cause), "Failed to get operation result within specified timeout: %s msec", timeout); } if (value == GenericUtils.NULL) { return null; } Class actualType = value.getClass(); if (expectedType.isAssignableFrom(actualType)) { return expectedType.cast(value); } if (value instanceof CancelFuture) { value = ((CancelFuture) value).getBackTrace(); if (value == null) { // Should not really occur value = new CancellationException("Operation was cancelled before"); } actualType = CancellationException.class; } if (Throwable.class.isAssignableFrom(actualType)) { Throwable t = ExceptionUtils.peelException((Throwable) value); if (t instanceof SshException) { throw new SshException(((SshException) t).getDisconnectCode(), t.getMessage(), t); } Throwable cause = t instanceof ConnectException ? t : ExceptionUtils.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. * @param options Optional {@link CancelOption} defining the behavior on time-out or interrupt. * @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, CancelOption... options) throws InterruptedIOException; @SuppressWarnings("unchecked") protected SshFutureListener asListener(Object o) { return (SshFutureListener) o; } protected void notifyListener(SshFutureListener l) { try { T arg = asT(); ThreadUtils.runAsInternal(() -> { l.operationComplete(arg); return null; }); } catch (Throwable t) { warn("notifyListener({}) failed ({}) to invoke {}: {}", this, t.getClass().getSimpleName(), l, t.getMessage(), 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