mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
It's not a Future, it's a Promise. Rename inspired by https://gist.github.com/959802.
Also Event now delegates to Promise instead of inheriting from it.
This commit is contained in:
@@ -17,16 +17,26 @@ package net.schmizz.concurrent;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public class FutureUtils {
|
public class ErrorDeliveryUtil {
|
||||||
|
|
||||||
public static void alertAll(Throwable x, Future... futures) {
|
public static void alertPromises(Throwable x, Promise... promises) {
|
||||||
for (Future f : futures)
|
for (Promise p : promises)
|
||||||
f.error(x);
|
p.deliverError(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void alertAll(Throwable x, Collection<? extends Future> futures) {
|
public static void alertPromises(Throwable x, Collection<? extends Promise> promises) {
|
||||||
for (Future f : futures)
|
for (Promise p : promises)
|
||||||
f.error(x);
|
p.deliverError(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void alertEvents(Throwable x, Event... events) {
|
||||||
|
for (Event e: events)
|
||||||
|
e.deliverError(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void alertEvents(Throwable x, Collection<? extends Event> events) {
|
||||||
|
for (Event e: events)
|
||||||
|
e.deliverError(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -18,21 +18,17 @@ package net.schmizz.concurrent;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/*
|
|
||||||
* Syntactic sugar around Future
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A kind of {@link Future} that caters to boolean values.
|
|
||||||
* <p/>
|
|
||||||
* An event can be set, cleared, or awaited, similar to Python's {@code threading.event}. The key difference is that a
|
* An event can be set, cleared, or awaited, similar to Python's {@code threading.event}. The key difference is that a
|
||||||
* waiter may be delivered an exception of parameterized type {@code T}. Furthermore, an event {@link #isSet()} when it
|
* waiter may be delivered an exception of parameterized type {@code T}.
|
||||||
* is not {@code null} i.e. it can be either {@code true} or {@code false} when set.
|
* <p/>
|
||||||
*
|
* Uses {@link Promise} under the hood.
|
||||||
* @see Future
|
|
||||||
*/
|
*/
|
||||||
public class Event<T extends Throwable>
|
public class Event<T extends Throwable> {
|
||||||
extends Future<Boolean, T> {
|
|
||||||
|
private static final Object SOME = new Object();
|
||||||
|
|
||||||
|
private final Promise<Object, T> promise;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates this event with given {@code name} and exception {@code chainer}. Allocates a new {@link
|
* Creates this event with given {@code name} and exception {@code chainer}. Allocates a new {@link
|
||||||
@@ -42,7 +38,7 @@ public class Event<T extends Throwable>
|
|||||||
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||||
*/
|
*/
|
||||||
public Event(String name, ExceptionChainer<T> chainer) {
|
public Event(String name, ExceptionChainer<T> chainer) {
|
||||||
super(name, chainer);
|
promise = new Promise<Object, T>(name, chainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,12 +49,30 @@ public class Event<T extends Throwable>
|
|||||||
* @param lock lock to use
|
* @param lock lock to use
|
||||||
*/
|
*/
|
||||||
public Event(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
public Event(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
||||||
super(name, chainer, lock);
|
promise = new Promise<Object, T>(name, chainer, lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets this event to be {@code true}. Short for {@code set(true)}. */
|
/** Sets this event to be {@code true}. Short for {@code set(true)}. */
|
||||||
public void set() {
|
public void set() {
|
||||||
super.set(true);
|
promise.deliver(SOME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clear this event. A cleared event {@code !isSet()}. */
|
||||||
|
public void clear() {
|
||||||
|
promise.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deliver the error {@code t} (after chaining) to any present or future waiters. */
|
||||||
|
public void deliverError(Throwable t) {
|
||||||
|
promise.deliverError(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this event is in a 'set' state. An event is set by a call to {@link set()} or {@link
|
||||||
|
* deliverError}
|
||||||
|
*/
|
||||||
|
public boolean isSet() {
|
||||||
|
return promise.isDelivered();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +82,7 @@ public class Event<T extends Throwable>
|
|||||||
*/
|
*/
|
||||||
public void await()
|
public void await()
|
||||||
throws T {
|
throws T {
|
||||||
super.get();
|
promise.retrieve();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,13 +95,13 @@ public class Event<T extends Throwable>
|
|||||||
*/
|
*/
|
||||||
public void await(long timeout, TimeUnit unit)
|
public void await(long timeout, TimeUnit unit)
|
||||||
throws T {
|
throws T {
|
||||||
super.get(timeout, unit);
|
promise.retrieve(timeout, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Await this event to have a definite {@code true} or {@code false} value, for {@code timeout} duration.
|
* Await this event to have a definite {@code true} or {@code false} value, for {@code timeout} duration.
|
||||||
*
|
* <p/>
|
||||||
* If the definite value is not available by the time timeout expires, returns {@code null}.
|
* If the definite value is not available when the timeout expires, returns {@code false}.
|
||||||
*
|
*
|
||||||
* @param timeout timeout
|
* @param timeout timeout
|
||||||
* @param unit the time unit for the timeout
|
* @param unit the time unit for the timeout
|
||||||
@@ -96,7 +110,32 @@ public class Event<T extends Throwable>
|
|||||||
*/
|
*/
|
||||||
public boolean tryAwait(long timeout, TimeUnit unit)
|
public boolean tryAwait(long timeout, TimeUnit unit)
|
||||||
throws T {
|
throws T {
|
||||||
return super.tryGet(timeout, unit) != null;
|
return promise.tryRetrieve(timeout, unit) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return whether there are any threads waiting on this event to be set. */
|
||||||
|
public boolean hasWaiters() {
|
||||||
|
return promise.hasWaiters();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return whether this event is in an error state i.e. has been delivered an error. */
|
||||||
|
public boolean inError() {
|
||||||
|
return promise.inError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Acquire the lock associated with this event. */
|
||||||
|
public void lock() {
|
||||||
|
promise.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Release the lock associated with this event. */
|
||||||
|
public void unlock() {
|
||||||
|
promise.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return promise.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,13 +24,13 @@ import java.util.concurrent.locks.Condition;
|
|||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents future data of the parameterized type {@code V} and allows waiting on it. An exception may also be
|
* Represents promised data of the parameterized type {@code V} and allows waiting on it. An exception may also be
|
||||||
* delivered to a waiter, and will be of the parameterized type {@code T}.
|
* delivered to a waiter, and will be of the parameterized type {@code T}.
|
||||||
* <p/>
|
* <p/>
|
||||||
* For atomic operations on a future, e.g. checking if a value is set and if it is not then setting it - in other words,
|
* For atomic operations on a promise, e.g. checking if a value is delivered and if it is not then setting it, the
|
||||||
* Compare-And-Set type operations - the associated lock for the future should be acquired while doing so.
|
* associated lock for the promise should be acquired while doing so.
|
||||||
*/
|
*/
|
||||||
public class Future<V, T extends Throwable> {
|
public class Promise<V, T extends Throwable> {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
@@ -43,24 +43,24 @@ public class Future<V, T extends Throwable> {
|
|||||||
private T pendingEx;
|
private T pendingEx;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates this future with given {@code name} and exception {@code chainer}. Allocates a new {@link
|
* Creates this promise with given {@code name} and exception {@code chainer}. Allocates a new {@link
|
||||||
* java.util.concurrent.locks.Lock lock} object for this future.
|
* java.util.concurrent.locks.Lock lock} object for this promise.
|
||||||
*
|
*
|
||||||
* @param name name of this future
|
* @param name name of this promise
|
||||||
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||||
*/
|
*/
|
||||||
public Future(String name, ExceptionChainer<T> chainer) {
|
public Promise(String name, ExceptionChainer<T> chainer) {
|
||||||
this(name, chainer, null);
|
this(name, chainer, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates this future with given {@code name}, exception {@code chainer}, and associated {@code lock}.
|
* Creates this promise with given {@code name}, exception {@code chainer}, and associated {@code lock}.
|
||||||
*
|
*
|
||||||
* @param name name of this future
|
* @param name name of this promise
|
||||||
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
* @param chainer {@link ExceptionChainer} that will be used for chaining exceptions
|
||||||
* @param lock lock to use
|
* @param lock lock to use
|
||||||
*/
|
*/
|
||||||
public Future(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
public Promise(String name, ExceptionChainer<T> chainer, ReentrantLock lock) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.chainer = chainer;
|
this.chainer = chainer;
|
||||||
this.lock = lock == null ? new ReentrantLock() : lock;
|
this.lock = lock == null ? new ReentrantLock() : lock;
|
||||||
@@ -68,73 +68,73 @@ public class Future<V, T extends Throwable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this future's value to {@code val}. Any waiters will be delivered this value.
|
* Set this promise's value to {@code val}. Any waiters will be delivered this value.
|
||||||
*
|
*
|
||||||
* @param val the value
|
* @param val the value
|
||||||
*/
|
*/
|
||||||
public void set(V val) {
|
public void deliver(V val) {
|
||||||
lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
log.debug("Setting <<{}>> to `{}`", name, val);
|
log.debug("Setting <<{}>> to `{}`", name, val);
|
||||||
this.val = val;
|
this.val = val;
|
||||||
cond.signalAll();
|
cond.signalAll();
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queues error that will be thrown in any waiting thread or any thread that attempts to wait on this future
|
* Queues error that will be thrown in any waiting thread or any thread that attempts to wait on this promise
|
||||||
* hereafter.
|
* hereafter.
|
||||||
*
|
*
|
||||||
* @param e the error
|
* @param e the error
|
||||||
*/
|
*/
|
||||||
public void error(Throwable e) {
|
public void deliverError(Throwable e) {
|
||||||
lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
pendingEx = chainer.chain(e);
|
pendingEx = chainer.chain(e);
|
||||||
cond.signalAll();
|
cond.signalAll();
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears this future by setting its value and queued exception to {@code null}. */
|
/** Clears this promise by setting its value and queued exception to {@code null}. */
|
||||||
public void clear() {
|
public void clear() {
|
||||||
lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
pendingEx = null;
|
pendingEx = null;
|
||||||
set(null);
|
deliver(null);
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait indefinitely for this future's value to be set.
|
* Wait indefinitely for this promise's value to be deliver.
|
||||||
*
|
*
|
||||||
* @return the value
|
* @return the value
|
||||||
*
|
*
|
||||||
* @throws T in case another thread informs the future of an error meanwhile
|
* @throws T in case another thread informs the promise of an error meanwhile
|
||||||
*/
|
*/
|
||||||
public V get()
|
public V retrieve()
|
||||||
throws T {
|
throws T {
|
||||||
return tryGet(0, TimeUnit.SECONDS);
|
return tryRetrieve(0, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for {@code timeout} duration for this future's value to be set.
|
* Wait for {@code timeout} duration for this promise's value to be deliver.
|
||||||
*
|
*
|
||||||
* @param timeout the timeout
|
* @param timeout the timeout
|
||||||
* @param unit time unit for the timeout
|
* @param unit time unit for the timeout
|
||||||
*
|
*
|
||||||
* @return the value
|
* @return the value
|
||||||
*
|
*
|
||||||
* @throws T in case another thread informs the future of an error meanwhile, or the timeout expires
|
* @throws T in case another thread informs the promise of an error meanwhile, or the timeout expires
|
||||||
*/
|
*/
|
||||||
public V get(long timeout, TimeUnit unit)
|
public V retrieve(long timeout, TimeUnit unit)
|
||||||
throws T {
|
throws T {
|
||||||
final V value = tryGet(timeout, unit);
|
final V value = tryRetrieve(timeout, unit);
|
||||||
if (value == null)
|
if (value == null)
|
||||||
throw chainer.chain(new TimeoutException("Timeout expired"));
|
throw chainer.chain(new TimeoutException("Timeout expired"));
|
||||||
else
|
else
|
||||||
@@ -142,20 +142,20 @@ public class Future<V, T extends Throwable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for {@code timeout} duration for this future's value to be set.
|
* Wait for {@code timeout} duration for this promise's value to be deliver.
|
||||||
*
|
* <p/>
|
||||||
* If the value is not set by the time the timeout expires, returns {@code null}.
|
* If the value is not deliver by the time the timeout expires, returns {@code null}.
|
||||||
*
|
*
|
||||||
* @param timeout the timeout
|
* @param timeout the timeout
|
||||||
* @param unit time unit for the timeout
|
* @param unit time unit for the timeout
|
||||||
*
|
*
|
||||||
* @return the value or {@code null}
|
* @return the value or {@code null}
|
||||||
*
|
*
|
||||||
* @throws T in case another thread informs the future of an error meanwhile
|
* @throws T in case another thread informs the promise of an error meanwhile
|
||||||
*/
|
*/
|
||||||
public V tryGet(long timeout, TimeUnit unit)
|
public V tryRetrieve(long timeout, TimeUnit unit)
|
||||||
throws T {
|
throws T {
|
||||||
lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
if (pendingEx != null)
|
if (pendingEx != null)
|
||||||
throw pendingEx;
|
throw pendingEx;
|
||||||
@@ -175,52 +175,46 @@ public class Future<V, T extends Throwable> {
|
|||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
throw chainer.chain(ie);
|
throw chainer.chain(ie);
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return whether this future has a value set, and no error waiting to pop. */
|
/** @return whether this promise has a value delivered, and no error waiting to pop. */
|
||||||
public boolean isSet() {
|
public boolean isDelivered() {
|
||||||
lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
return pendingEx == null && val != null;
|
return pendingEx == null && val != null;
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return whether this future currently has an error set. */
|
/** @return whether this promise has been delivered an error. */
|
||||||
public boolean hasError() {
|
public boolean inError() {
|
||||||
lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
return pendingEx != null;
|
return pendingEx != null;
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return whether this future has threads waiting on it. */
|
/** @return whether this promise has threads waiting on it. */
|
||||||
public boolean hasWaiters() {
|
public boolean hasWaiters() {
|
||||||
lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
return lock.hasWaiters(cond);
|
return lock.hasWaiters(cond);
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Acquire the lock associated with this promise. */
|
||||||
* Lock using the associated lock. Use as part of a {@code try-finally} construct in conjunction with {@link
|
|
||||||
* #unlock()}.
|
|
||||||
*/
|
|
||||||
public void lock() {
|
public void lock() {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Release the lock associated with this promise. */
|
||||||
* Unlock using the associated lock. Use as part of a {@code try-finally} construct in conjunction with {@link
|
|
||||||
* #lock()}.
|
|
||||||
*/
|
|
||||||
public void unlock() {
|
public void unlock() {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ public class StreamCopier {
|
|||||||
doneEvent.set();
|
doneEvent.set();
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
log.error("In pipe from {} to {}: " + ioe.toString(), in, out);
|
log.error("In pipe from {} to {}: " + ioe.toString(), in, out);
|
||||||
doneEvent.error(ioe);
|
doneEvent.deliverError(ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.connection;
|
package net.schmizz.sshj.connection;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Future;
|
import net.schmizz.concurrent.Promise;
|
||||||
import net.schmizz.sshj.common.SSHPacket;
|
import net.schmizz.sshj.common.SSHPacket;
|
||||||
import net.schmizz.sshj.connection.channel.Channel;
|
import net.schmizz.sshj.connection.channel.Channel;
|
||||||
import net.schmizz.sshj.connection.channel.OpenFailException;
|
import net.schmizz.sshj.connection.channel.OpenFailException;
|
||||||
@@ -89,12 +89,12 @@ public interface Connection {
|
|||||||
* @param wantReply whether a reply is requested
|
* @param wantReply whether a reply is requested
|
||||||
* @param specifics {@link SSHPacket} containing fields specific to the request
|
* @param specifics {@link SSHPacket} containing fields specific to the request
|
||||||
*
|
*
|
||||||
* @return a {@link Future} for the reply data (in case {@code wantReply} is true) which allows waiting on the
|
* @return a {@link net.schmizz.concurrent.Promise} for the reply data (in case {@code wantReply} is true) which allows waiting on the
|
||||||
* reply, or {@code null} if a reply is not requested.
|
* reply, or {@code null} if a reply is not requested.
|
||||||
*
|
*
|
||||||
* @throws TransportException if there is an error sending the request
|
* @throws TransportException if there is an error sending the request
|
||||||
*/
|
*/
|
||||||
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
public Promise<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
||||||
byte[] specifics)
|
byte[] specifics)
|
||||||
throws TransportException;
|
throws TransportException;
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.connection;
|
package net.schmizz.sshj.connection;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Future;
|
import net.schmizz.concurrent.Promise;
|
||||||
import net.schmizz.concurrent.FutureUtils;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.sshj.AbstractService;
|
import net.schmizz.sshj.AbstractService;
|
||||||
import net.schmizz.sshj.common.DisconnectReason;
|
import net.schmizz.sshj.common.DisconnectReason;
|
||||||
import net.schmizz.sshj.common.ErrorNotifiable;
|
import net.schmizz.sshj.common.ErrorNotifiable;
|
||||||
@@ -30,7 +30,6 @@ import net.schmizz.sshj.connection.channel.forwarded.ForwardedChannelOpener;
|
|||||||
import net.schmizz.sshj.transport.Transport;
|
import net.schmizz.sshj.transport.Transport;
|
||||||
import net.schmizz.sshj.transport.TransportException;
|
import net.schmizz.sshj.transport.TransportException;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
@@ -50,7 +49,7 @@ public class ConnectionImpl
|
|||||||
|
|
||||||
private final Map<String, ForwardedChannelOpener> openers = new ConcurrentHashMap<String, ForwardedChannelOpener>();
|
private final Map<String, ForwardedChannelOpener> openers = new ConcurrentHashMap<String, ForwardedChannelOpener>();
|
||||||
|
|
||||||
private final Queue<Future<SSHPacket, ConnectionException>> globalReqFutures = new LinkedList<Future<SSHPacket, ConnectionException>>();
|
private final Queue<Promise<SSHPacket, ConnectionException>> globalReqPromises = new LinkedList<Promise<SSHPacket, ConnectionException>>();
|
||||||
|
|
||||||
private int windowSize = 2048 * 1024;
|
private int windowSize = 2048 * 1024;
|
||||||
private int maxPacketSize = 32 * 1024;
|
private int maxPacketSize = 32 * 1024;
|
||||||
@@ -180,34 +179,34 @@ public class ConnectionImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
public Promise<SSHPacket, ConnectionException> sendGlobalRequest(String name, boolean wantReply,
|
||||||
byte[] specifics)
|
byte[] specifics)
|
||||||
throws TransportException {
|
throws TransportException {
|
||||||
synchronized (globalReqFutures) {
|
synchronized (globalReqPromises) {
|
||||||
log.info("Making global request for `{}`", name);
|
log.info("Making global request for `{}`", name);
|
||||||
trans.write(new SSHPacket(Message.GLOBAL_REQUEST).putString(name)
|
trans.write(new SSHPacket(Message.GLOBAL_REQUEST).putString(name)
|
||||||
.putBoolean(wantReply).putRawBytes(specifics));
|
.putBoolean(wantReply).putRawBytes(specifics));
|
||||||
|
|
||||||
Future<SSHPacket, ConnectionException> future = null;
|
Promise<SSHPacket, ConnectionException> promise = null;
|
||||||
if (wantReply) {
|
if (wantReply) {
|
||||||
future = new Future<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer);
|
promise = new Promise<SSHPacket, ConnectionException>("global req for " + name, ConnectionException.chainer);
|
||||||
globalReqFutures.add(future);
|
globalReqPromises.add(promise);
|
||||||
}
|
}
|
||||||
return future;
|
return promise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gotGlobalReqResponse(SSHPacket response)
|
private void gotGlobalReqResponse(SSHPacket response)
|
||||||
throws ConnectionException {
|
throws ConnectionException {
|
||||||
synchronized (globalReqFutures) {
|
synchronized (globalReqPromises) {
|
||||||
Future<SSHPacket, ConnectionException> gr = globalReqFutures.poll();
|
Promise<SSHPacket, ConnectionException> gr = globalReqPromises.poll();
|
||||||
if (gr == null)
|
if (gr == null)
|
||||||
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
|
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
|
||||||
"Got a global request response when none was requested");
|
"Got a global request response when none was requested");
|
||||||
else if (response == null)
|
else if (response == null)
|
||||||
gr.error(new ConnectionException("Global request [" + gr + "] failed"));
|
gr.deliverError(new ConnectionException("Global request [" + gr + "] failed"));
|
||||||
else
|
else
|
||||||
gr.set(response);
|
gr.deliver(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,9 +234,9 @@ public class ConnectionImpl
|
|||||||
@Override
|
@Override
|
||||||
public void notifyError(SSHException error) {
|
public void notifyError(SSHException error) {
|
||||||
super.notifyError(error);
|
super.notifyError(error);
|
||||||
synchronized (globalReqFutures) {
|
synchronized (globalReqPromises) {
|
||||||
FutureUtils.alertAll(error, globalReqFutures);
|
ErrorDeliveryUtil.alertPromises(error, globalReqPromises);
|
||||||
globalReqFutures.clear();
|
globalReqPromises.clear();
|
||||||
}
|
}
|
||||||
ErrorNotifiable.Util.alertAll(error, channels.values());
|
ErrorNotifiable.Util.alertAll(error, channels.values());
|
||||||
channels.clear();
|
channels.clear();
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
package net.schmizz.sshj.connection.channel;
|
package net.schmizz.sshj.connection.channel;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.concurrent.FutureUtils;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.Buffer;
|
||||||
import net.schmizz.sshj.common.ByteArrayUtils;
|
import net.schmizz.sshj.common.ByteArrayUtils;
|
||||||
import net.schmizz.sshj.common.DisconnectReason;
|
import net.schmizz.sshj.common.DisconnectReason;
|
||||||
@@ -236,8 +236,8 @@ public abstract class AbstractChannel
|
|||||||
public void notifyError(SSHException error) {
|
public void notifyError(SSHException error) {
|
||||||
log.debug("Channel #{} got notified of {}", getID(), error.toString());
|
log.debug("Channel #{} got notified of {}", getID(), error.toString());
|
||||||
|
|
||||||
FutureUtils.alertAll(error, open, close);
|
ErrorDeliveryUtil.alertEvents(error, open, close);
|
||||||
FutureUtils.alertAll(error, chanReqResponseEvents);
|
ErrorDeliveryUtil.alertEvents(error, chanReqResponseEvents);
|
||||||
|
|
||||||
in.notifyError(error);
|
in.notifyError(error);
|
||||||
out.notifyError(error);
|
out.notifyError(error);
|
||||||
@@ -258,7 +258,7 @@ public abstract class AbstractChannel
|
|||||||
try {
|
try {
|
||||||
sendClose();
|
sendClose();
|
||||||
} catch (TransportException e) {
|
} catch (TransportException e) {
|
||||||
if (!close.hasError())
|
if (!close.inError())
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
close.await(conn.getTimeout(), TimeUnit.SECONDS);
|
close.await(conn.getTimeout(), TimeUnit.SECONDS);
|
||||||
@@ -372,7 +372,7 @@ public abstract class AbstractChannel
|
|||||||
if (success)
|
if (success)
|
||||||
responseEvent.set();
|
responseEvent.set();
|
||||||
else
|
else
|
||||||
responseEvent.error(new ConnectionException("Request failed"));
|
responseEvent.deliverError(new ConnectionException("Request failed"));
|
||||||
} else
|
} else
|
||||||
throw new ConnectionException(
|
throw new ConnectionException(
|
||||||
DisconnectReason.PROTOCOL_ERROR,
|
DisconnectReason.PROTOCOL_ERROR,
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public abstract class AbstractDirectChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void gotOpenFailure(SSHPacket buf) {
|
private void gotOpenFailure(SSHPacket buf) {
|
||||||
open.error(new OpenFailException(getType(), buf.readInt(), buf.readString()));
|
open.deliverError(new OpenFailException(getType(), buf.readInt(), buf.readString()));
|
||||||
finishOff();
|
finishOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ public class RemotePortForwarder
|
|||||||
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putInt(forward.port)
|
final byte[] specifics = new Buffer.PlainBuffer().putString(forward.address).putInt(forward.port)
|
||||||
.getCompactData();
|
.getCompactData();
|
||||||
return conn.sendGlobalRequest(reqName, true, specifics)
|
return conn.sendGlobalRequest(reqName, true, specifics)
|
||||||
.get(conn.getTimeout(), TimeUnit.SECONDS);
|
.retrieve(conn.getTimeout(), TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the active forwards. */
|
/** @return the active forwards. */
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.sftp;
|
package net.schmizz.sshj.sftp;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Future;
|
import net.schmizz.concurrent.Promise;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ public class PacketReader
|
|||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private final Map<Long, Future<Response, SFTPException>> futures = new ConcurrentHashMap<Long, Future<Response, SFTPException>>();
|
private final Map<Long, Promise<Response, SFTPException>> promises = new ConcurrentHashMap<Long, Promise<Response, SFTPException>>();
|
||||||
private final SFTPPacket<Response> packet = new SFTPPacket<Response>();
|
private final SFTPPacket<Response> packet = new SFTPPacket<Response>();
|
||||||
private final byte[] lenBuf = new byte[4];
|
private final byte[] lenBuf = new byte[4];
|
||||||
private final SFTPEngine engine;
|
private final SFTPEngine engine;
|
||||||
@@ -85,25 +85,25 @@ public class PacketReader
|
|||||||
handle();
|
handle();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
for (Future<Response, SFTPException> future : futures.values())
|
for (Promise<Response, SFTPException> promise : promises.values())
|
||||||
future.error(e);
|
promise.deliverError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle()
|
public void handle()
|
||||||
throws SFTPException {
|
throws SFTPException {
|
||||||
Response resp = new Response(packet, engine.getOperativeProtocolVersion());
|
Response resp = new Response(packet, engine.getOperativeProtocolVersion());
|
||||||
Future<Response, SFTPException> future = futures.remove(resp.getRequestID());
|
Promise<Response, SFTPException> promise = promises.remove(resp.getRequestID());
|
||||||
log.debug("Received {} packet", resp.getType());
|
log.debug("Received {} packet", resp.getType());
|
||||||
if (future == null)
|
if (promise == null)
|
||||||
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
|
throw new SFTPException("Received [" + resp.readType() + "] response for request-id " + resp.getRequestID()
|
||||||
+ ", no such request was made");
|
+ ", no such request was made");
|
||||||
else
|
else
|
||||||
future.set(resp);
|
promise.deliver(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expectResponseTo(Request req) {
|
public void expectResponseTo(Request req) {
|
||||||
futures.put(req.getRequestID(), req.getResponseFuture());
|
promises.put(req.getRequestID(), req.getResponsePromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,20 +15,20 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.sftp;
|
package net.schmizz.sshj.sftp;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Future;
|
import net.schmizz.concurrent.Promise;
|
||||||
|
|
||||||
public class Request
|
public class Request
|
||||||
extends SFTPPacket<Request> {
|
extends SFTPPacket<Request> {
|
||||||
|
|
||||||
private final PacketType type;
|
private final PacketType type;
|
||||||
private final long reqID;
|
private final long reqID;
|
||||||
private final Future<Response, SFTPException> responseFuture;
|
private final Promise<Response, SFTPException> responsePromise;
|
||||||
|
|
||||||
public Request(PacketType type, long reqID) {
|
public Request(PacketType type, long reqID) {
|
||||||
super(type);
|
super(type);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.reqID = reqID;
|
this.reqID = reqID;
|
||||||
responseFuture = new Future<Response, SFTPException>("sftp / " + reqID, SFTPException.chainer);
|
responsePromise = new Promise<Response, SFTPException>("sftp / " + reqID, SFTPException.chainer);
|
||||||
putInt(reqID);
|
putInt(reqID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,8 +40,8 @@ public class Request
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Future<Response, SFTPException> getResponseFuture() {
|
public Promise<Response, SFTPException> getResponsePromise() {
|
||||||
return responseFuture;
|
return responsePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public class SFTPEngine
|
|||||||
reader.expectResponseTo(req);
|
reader.expectResponseTo(req);
|
||||||
log.debug("Sending {}", req);
|
log.debug("Sending {}", req);
|
||||||
transmit(req);
|
transmit(req);
|
||||||
return req.getResponseFuture().get(timeout, TimeUnit.SECONDS);
|
return req.getResponsePromise().retrieve(timeout, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
|
public RemoteFile open(String path, Set<OpenMode> modes, FileAttributes fa)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.concurrent.FutureUtils;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.sshj.common.Buffer;
|
import net.schmizz.sshj.common.Buffer;
|
||||||
import net.schmizz.sshj.common.DisconnectReason;
|
import net.schmizz.sshj.common.DisconnectReason;
|
||||||
import net.schmizz.sshj.common.ErrorNotifiable;
|
import net.schmizz.sshj.common.ErrorNotifiable;
|
||||||
@@ -397,7 +397,7 @@ final class KeyExchanger
|
|||||||
@Override
|
@Override
|
||||||
public void notifyError(SSHException error) {
|
public void notifyError(SSHException error) {
|
||||||
log.debug("Got notified of {}", error.toString());
|
log.debug("Got notified of {}", error.toString());
|
||||||
FutureUtils.alertAll(error, kexInitSent, done);
|
ErrorDeliveryUtil.alertEvents(error, kexInitSent, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
package net.schmizz.sshj.transport;
|
package net.schmizz.sshj.transport;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Event;
|
||||||
import net.schmizz.concurrent.FutureUtils;
|
import net.schmizz.concurrent.ErrorDeliveryUtil;
|
||||||
import net.schmizz.sshj.AbstractService;
|
import net.schmizz.sshj.AbstractService;
|
||||||
import net.schmizz.sshj.Config;
|
import net.schmizz.sshj.Config;
|
||||||
import net.schmizz.sshj.Service;
|
import net.schmizz.sshj.Service;
|
||||||
@@ -562,7 +562,7 @@ public final class TransportImpl
|
|||||||
|
|
||||||
disconnectListener.notifyDisconnect(causeOfDeath.getDisconnectReason());
|
disconnectListener.notifyDisconnect(causeOfDeath.getDisconnectReason());
|
||||||
|
|
||||||
FutureUtils.alertAll(causeOfDeath, close, serviceAccept);
|
ErrorDeliveryUtil.alertEvents(causeOfDeath, close, serviceAccept);
|
||||||
kexer.notifyError(causeOfDeath);
|
kexer.notifyError(causeOfDeath);
|
||||||
getService().notifyError(causeOfDeath);
|
getService().notifyError(causeOfDeath);
|
||||||
setService(nullService);
|
setService(nullService);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.userauth;
|
package net.schmizz.sshj.userauth;
|
||||||
|
|
||||||
import net.schmizz.concurrent.Event;
|
import net.schmizz.concurrent.Promise;
|
||||||
import net.schmizz.sshj.AbstractService;
|
import net.schmizz.sshj.AbstractService;
|
||||||
import net.schmizz.sshj.Service;
|
import net.schmizz.sshj.Service;
|
||||||
import net.schmizz.sshj.common.DisconnectReason;
|
import net.schmizz.sshj.common.DisconnectReason;
|
||||||
@@ -42,7 +42,8 @@ public class UserAuthImpl
|
|||||||
|
|
||||||
private final Deque<UserAuthException> savedEx = new ArrayDeque<UserAuthException>();
|
private final Deque<UserAuthException> savedEx = new ArrayDeque<UserAuthException>();
|
||||||
|
|
||||||
private final Event<UserAuthException> result = new Event<UserAuthException>("userauth result", UserAuthException.chainer);
|
private final Promise<Boolean, UserAuthException> result
|
||||||
|
= new Promise<Boolean, UserAuthException>("userauth result", UserAuthException.chainer);
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private AuthMethod currentMethod;
|
private AuthMethod currentMethod;
|
||||||
@@ -172,7 +173,7 @@ public class UserAuthImpl
|
|||||||
@Override
|
@Override
|
||||||
public void notifyError(SSHException error) {
|
public void notifyError(SSHException error) {
|
||||||
super.notifyError(error);
|
super.notifyError(error);
|
||||||
result.error(error);
|
result.deliverError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearState() {
|
private void clearState() {
|
||||||
@@ -194,14 +195,14 @@ public class UserAuthImpl
|
|||||||
currentMethod.request();
|
currentMethod.request();
|
||||||
else {
|
else {
|
||||||
saveException(currentMethod.getName() + " auth failed");
|
saveException(currentMethod.getName() + " auth failed");
|
||||||
result.set(false);
|
result.deliver(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gotSuccess() {
|
private void gotSuccess() {
|
||||||
trans.setAuthenticated(); // So it can put delayed compression into force if applicable
|
trans.setAuthenticated(); // So it can put delayed compression into force if applicable
|
||||||
trans.setService(nextService); // We aren't in charge anymore, next service is
|
trans.setService(nextService); // We aren't in charge anymore, next service is
|
||||||
result.set(true);
|
result.deliver(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gotUnknown(Message msg, SSHPacket buf)
|
private void gotUnknown(Message msg, SSHPacket buf)
|
||||||
@@ -215,7 +216,7 @@ public class UserAuthImpl
|
|||||||
try {
|
try {
|
||||||
currentMethod.handle(msg, buf);
|
currentMethod.handle(msg, buf);
|
||||||
} catch (UserAuthException e) {
|
} catch (UserAuthException e) {
|
||||||
result.error(e);
|
result.deliverError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ public class UserAuthImpl
|
|||||||
result.clear();
|
result.clear();
|
||||||
meth.init(this);
|
meth.init(this);
|
||||||
meth.request();
|
meth.request();
|
||||||
return result.get(timeout, TimeUnit.SECONDS);
|
return result.retrieve(timeout, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user