You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2016/02/20 19:18:57 UTC
mina-sshd git commit: [SSHD-652] Allow pre-registering CloseFuture
listeners without having to call "close" method
Repository: mina-sshd
Updated Branches:
refs/heads/master d3e5d427e -> ddfe6f6b6
[SSHD-652] Allow pre-registering CloseFuture listeners without having to call "close" method
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/ddfe6f6b
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/ddfe6f6b
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/ddfe6f6b
Branch: refs/heads/master
Commit: ddfe6f6b6de9c5ba3b798c319785d8304f794551
Parents: d3e5d42
Author: Lyor Goldstein <ly...@gmail.com>
Authored: Sat Feb 20 20:19:35 2016 +0200
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Sat Feb 20 20:19:35 2016 +0200
----------------------------------------------------------------------
.../java/org/apache/sshd/common/Closeable.java | 28 +++++++--
.../sshd/common/channel/AbstractChannel.java | 10 ++++
.../org/apache/sshd/common/channel/Channel.java | 6 +-
.../sshd/common/future/AbstractSshFuture.java | 6 +-
.../sshd/common/future/DefaultSshFuture.java | 62 +++++++++++---------
.../sshd/common/future/SshFutureListener.java | 1 -
.../apache/sshd/common/io/mina/MinaSession.java | 16 ++++-
.../util/closeable/AbstractCloseable.java | 12 ++++
.../common/util/closeable/CloseableUtils.java | 7 ---
.../common/util/closeable/SimpleCloseable.java | 11 ++++
.../sshd/server/channel/ChannelSession.java | 14 +++++
.../common/session/AbstractSessionTest.java | 33 ++++++++++-
.../util/closeable/CloseableUtilsTest.java | 31 ++++++++++
.../sshd/server/channel/ChannelSessionTest.java | 25 ++++++++
14 files changed, 216 insertions(+), 46 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java b/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
index 8d7194a..a7278fc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
@@ -21,11 +21,13 @@ package org.apache.sshd.common;
import java.nio.channels.Channel;
import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
/**
* A {@code Closeable} is a resource that can be closed.
* The close method is invoked to release resources that the object is
- * holding.
+ * holding. The user can pre-register listeners to be notified
+ * when resource close is completed (successfully or otherwise)
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@@ -39,11 +41,28 @@ public interface Closeable extends Channel {
*
* @param immediately <code>true</code> if the resource should be shut down abruptly,
* <code>false</code> for a graceful close
- * @return a future
+ * @return a {@link CloseFuture} representing the close request
*/
CloseFuture close(boolean immediately);
/**
+ * Pre-register a listener to be informed when resource is closed. If
+ * resource is already closed, the listener will be invoked immediately
+ * and not registered for future notification
+ *
+ * @param listener The notification {@link #SshFutureListener} - never {@code null}
+ */
+ void addCloseFutureListener(SshFutureListener<CloseFuture> listener);
+
+ /**
+ * Remove a pre-registered close event listener
+ *
+ * @param listener The register {@link #SshFutureListener} - never {@code null}.
+ * Ignored if not registered or resource already closed
+ */
+ void removeCloseFutureListener(SshFutureListener<CloseFuture> listener);
+
+ /**
* Returns <code>true</code> if this object has been closed.
*
* @return <code>true</code> if closing
@@ -52,9 +71,8 @@ public interface Closeable extends Channel {
/**
* Returns <code>true</code> if the {@link #close(boolean)} method
- * has been called.
- * Note that this method will return <code>true</code> even if
- * this {@link #isClosed()} returns <code>true</code>.
+ * has been called. Note that this method will return <code>true</code>
+ * even if this {@link #isClosed()} returns <code>true</code>.
*
* @return <code>true</code> if closing
*/
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
index 125e629..e560e54 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
@@ -455,6 +455,16 @@ public abstract class AbstractChannel
}
@Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ gracefulFuture.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ gracefulFuture.removeListener(listener);
+ }
+
+ @Override
public boolean isClosing() {
return closing.get();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
index 0842953..dab6f72 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
@@ -34,7 +34,11 @@ import org.apache.sshd.common.util.buffer.Buffer;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface Channel extends ChannelListenerManager, PropertyResolver, AttributeStore, Closeable {
+public interface Channel
+ extends ChannelListenerManager,
+ PropertyResolver,
+ AttributeStore,
+ Closeable {
// Known types of channels
String CHANNEL_EXEC = "exec";
String CHANNEL_SHELL = "shell";
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
index 8efd65f..610b305 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
@@ -164,7 +164,11 @@ public abstract class AbstractSshFuture<T extends SshFuture> extends AbstractLog
try {
l.operationComplete(asT());
} catch (Throwable t) {
- log.warn("Listener threw an exception", t);
+ log.warn("notifyListener({}) failed ({}) to invoke {}: {}",
+ this, t.getClass().getSimpleName(), l, t.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("notifyListener(" + this + ")[" + l + "] invocation failure details", t);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
index 8f31339..4c35210 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
@@ -119,21 +119,20 @@ public class DefaultSshFuture<T extends SshFuture> extends AbstractSshFuture<T>
ValidateUtils.checkNotNull(listener, "Missing listener argument");
boolean notifyNow = false;
synchronized (lock) {
+ // if already have a result don't register the listener and invoke it directly
if (result != null) {
notifyNow = true;
- } else {
- if (listeners == null) {
- listeners = listener;
- } else if (listeners instanceof SshFutureListener) {
- listeners = new Object[]{listeners, listener};
- } else {
- Object[] ol = (Object[]) listeners;
- int l = ol.length;
- Object[] nl = new Object[l + 1];
- System.arraycopy(ol, 0, nl, 0, l);
- nl[l] = listener;
- listeners = nl;
- }
+ } else if (listeners == null) {
+ listeners = listener; // 1st listener ?
+ } else if (listeners instanceof SshFutureListener) {
+ listeners = new Object[]{listeners, listener};
+ } else { // increase array of registered listeners
+ Object[] ol = (Object[]) listeners;
+ int l = ol.length;
+ Object[] nl = new Object[l + 1];
+ System.arraycopy(ol, 0, nl, 0, l);
+ nl[l] = listener;
+ listeners = nl;
}
}
@@ -148,18 +147,22 @@ public class DefaultSshFuture<T extends SshFuture> extends AbstractSshFuture<T>
ValidateUtils.checkNotNull(listener, "No listener provided");
synchronized (lock) {
- if (result == null) {
- if (listeners != null) {
- if (listeners == listener) {
- listeners = null;
- } else {
- int l = Array.getLength(listeners);
- for (int i = 0; i < l; i++) {
- if (Array.get(listeners, i) == listener) {
- Array.set(listeners, i, null);
- break;
- }
- }
+ if (result != null) {
+ return asT(); // the train has already left the station...
+ }
+
+ if (listeners == null) {
+ return asT(); // no registered instances anyway
+ }
+
+ if (listeners == listener) {
+ listeners = null; // the one and only
+ } else if (!(listeners instanceof SshFutureListener)) {
+ int l = Array.getLength(listeners);
+ for (int i = 0; i < l; i++) {
+ if (Array.get(listeners, i) == listener) {
+ Array.set(listeners, i, null);
+ break;
}
}
}
@@ -169,9 +172,12 @@ public class DefaultSshFuture<T extends SshFuture> extends AbstractSshFuture<T>
}
protected void notifyListeners() {
- // There won't be any visibility problem or concurrent modification
- // because 'ready' flag will be checked against both addListener and
- // removeListener calls.
+ /*
+ * There won't be any visibility problem or concurrent modification
+ * because result value is checked in both addListener and
+ * removeListener calls under lock. If the result is already set then
+ * both methods will not modify the internal listeners
+ */
if (listeners != null) {
if (listeners instanceof SshFutureListener) {
notifyListener(asListener(listeners));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
index ede2fcf..c17f551 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
@@ -38,5 +38,4 @@ public interface SshFutureListener<T extends SshFuture> extends EventListener {
* callback.
*/
void operationComplete(T future);
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java b/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
index dfd0bb2..24c26a6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
@@ -25,7 +25,9 @@ import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.future.WriteFuture;
import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.io.AbstractIoWriteFuture;
import org.apache.sshd.common.io.IoService;
import org.apache.sshd.common.io.IoSession;
@@ -90,6 +92,9 @@ public class MinaSession extends AbstractInnerCloseable implements IoSession {
protected Closeable getInnerCloseable() {
return new IoBaseCloseable() {
@SuppressWarnings("synthetic-access")
+ private final DefaultCloseFuture future = new DefaultCloseFuture(lock);
+
+ @SuppressWarnings("synthetic-access")
@Override
public boolean isClosing() {
return session.isClosing();
@@ -101,10 +106,19 @@ public class MinaSession extends AbstractInnerCloseable implements IoSession {
return !session.isConnected();
}
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ future.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ future.removeListener(listener);
+ }
+
@SuppressWarnings("synthetic-access")
@Override
public org.apache.sshd.common.future.CloseFuture close(boolean immediately) {
- final DefaultCloseFuture future = new DefaultCloseFuture(lock);
session.close(false).addListener(new IoFutureListener<IoFuture>() {
@Override
public void operationComplete(IoFuture f) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
index e32cf3d..c3780cb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
@@ -40,10 +40,12 @@ public abstract class AbstractCloseable extends IoBaseCloseable {
* Lock object for this session state
*/
protected final Object lock = new Object();
+
/**
* State of this object
*/
protected final AtomicReference<AbstractCloseable.State> state = new AtomicReference<>(State.Opened);
+
/**
* A future that will be set 'closed' when the object is actually closed
*/
@@ -58,6 +60,16 @@ public abstract class AbstractCloseable extends IoBaseCloseable {
}
@Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeFuture.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeFuture.removeListener(listener);
+ }
+
+ @Override
public CloseFuture close(boolean immediately) {
if (immediately) {
if (state.compareAndSet(State.Opened, State.Immediate)
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java
index e0223c1..4d5d885 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java
@@ -26,7 +26,6 @@ import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
/**
* Utility class to help with {@link Closeable}s.
@@ -72,10 +71,4 @@ public final class CloseableUtils {
}
}
}
-
- public static CloseFuture closed() {
- CloseFuture future = new DefaultCloseFuture(null);
- future.setClosed();
- return future;
- }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
index 7bda0c6..8978972 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
@@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.sshd.common.future.CloseFuture;
import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -47,6 +48,16 @@ public class SimpleCloseable extends IoBaseCloseable {
}
@Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ future.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ future.removeListener(listener);
+ }
+
+ @Override
public CloseFuture close(boolean immediately) {
if (closing.compareAndSet(false, true)) {
doClose(immediately);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index 325b746..45a8f4f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -123,6 +123,10 @@ public class ChannelSession extends AbstractServerChannel {
}
public class CommandCloseable extends IoBaseCloseable {
+ public CommandCloseable() {
+ super();
+ }
+
@Override
public boolean isClosed() {
return commandExitFuture.isClosed();
@@ -134,6 +138,16 @@ public class ChannelSession extends AbstractServerChannel {
}
@Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ commandExitFuture.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ commandExitFuture.removeListener(listener);
+ }
+
+ @Override
public CloseFuture close(boolean immediately) {
if (immediately || (commandInstance == null)) {
commandExitFuture.setClosed();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java
index 886f08f..da685f8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/session/AbstractSessionTest.java
@@ -25,12 +25,15 @@ import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.channel.IoWriteFutureImpl;
import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.io.IoService;
import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.io.IoWriteFuture;
@@ -167,12 +170,37 @@ public class AbstractSessionTest extends BaseTestSupport {
assertEquals("Mismatched number of ignore messages", Byte.SIZE, numIgnores);
}
+ @Test // see SSHD-652
+ public void testCloseFutureListenerRegistration() throws Exception {
+ final AtomicInteger closeCount = new AtomicInteger();
+ session.addCloseFutureListener(new SshFutureListener<CloseFuture>() {
+ @Override
+ public void operationComplete(CloseFuture future) {
+ assertTrue("Future not marted as closed", future.isClosed());
+ assertEquals("Unexpected multiple call to callback", 1, closeCount.incrementAndGet());
+ }
+ });
+ session.close();
+ assertEquals("Close listener not called", 1, closeCount.get());
+ }
+
public static class MyIoSession implements IoSession {
private final Queue<Buffer> outgoing = new LinkedBlockingQueue<>();
private final AtomicBoolean open = new AtomicBoolean(true);
+ private final CloseFuture closeFuture;
public MyIoSession() {
- super();
+ closeFuture = new DefaultCloseFuture(open);
+ }
+
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeFuture.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeFuture.addListener(listener);
}
public Queue<Buffer> getOutgoingMessages() {
@@ -242,9 +270,10 @@ public class AbstractSessionTest extends BaseTestSupport {
public CloseFuture close(boolean immediately) {
if (open.getAndSet(false)) {
outgoing.clear();
+ closeFuture.setClosed();
}
- return null;
+ return closeFuture;
}
@Override
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
index 8d8d807..8605d2d 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.future.CloseFuture;
import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.util.threads.ThreadUtils;
import org.apache.sshd.util.test.BaseTestSupport;
import org.junit.FixMethodOrder;
@@ -53,6 +54,16 @@ public class CloseableUtilsTest extends BaseTestSupport {
}
@Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to addCloseFutureListener");
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to removeCloseFutureListener");
+ }
+
+ @Override
public boolean isClosed() {
return true;
}
@@ -75,6 +86,16 @@ public class CloseableUtilsTest extends BaseTestSupport {
}
@Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to addCloseFutureListener");
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to removeCloseFutureListener");
+ }
+
+ @Override
public boolean isClosed() {
return false;
}
@@ -100,6 +121,16 @@ public class CloseableUtilsTest extends BaseTestSupport {
}
@Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to addCloseFutureListener");
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to removeCloseFutureListener");
+ }
+
+ @Override
public boolean isClosed() {
return false;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ddfe6f6b/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java
index addfa75..a000104 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java
@@ -21,10 +21,13 @@ package org.apache.sshd.server.channel;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.channel.ChannelAsyncOutputStream;
import org.apache.sshd.common.channel.Window;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.util.test.BaseTestSupport;
@@ -65,4 +68,26 @@ public class ChannelSessionTest extends BaseTestSupport {
assertTrue("Expanded ?", expanded.get());
}
}
+
+ @Test // see SSHD-652
+ public void testCloseFutureListenerRegistration() throws Exception {
+ final AtomicInteger closeCount = new AtomicInteger();
+ try (ChannelSession session = new ChannelSession() {
+ {
+ Window wRemote = getRemoteWindow();
+ wRemote.init(PropertyResolverUtils.toPropertyResolver(Collections.<String,Object>emptyMap()));
+ }
+ }) {
+ session.addCloseFutureListener(new SshFutureListener<CloseFuture>() {
+ @Override
+ public void operationComplete(CloseFuture future) {
+ assertTrue("Future not marted as closed", future.isClosed());
+ assertEquals("Unexpected multiple call to callback", 1, closeCount.incrementAndGet());
+ }
+ });
+ session.close();
+ }
+
+ assertEquals("Close listener not called", 1, closeCount.get());
+ }
}
\ No newline at end of file