You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2015/02/10 11:32:25 UTC

mina-sshd git commit: [SSHD-403] Lay down the groundwork for more event listeners

Repository: mina-sshd
Updated Branches:
  refs/heads/master 6af9457d3 -> 7f88d3a18


[SSHD-403] Lay down the groundwork for more event listeners

Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/7f88d3a1
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/7f88d3a1
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/7f88d3a1

Branch: refs/heads/master
Commit: 7f88d3a18f9a4df77ae429ae724f4fd80b8685f5
Parents: 6af9457
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Feb 10 11:32:10 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Feb 10 11:32:18 2015 +0100

----------------------------------------------------------------------
 .../org/apache/sshd/common/SessionListener.java |   4 +-
 .../sshd/common/session/AbstractSession.java    |  17 ++--
 .../common/session/AbstractSessionFactory.java  |   4 +
 .../sshd/common/util/EventListenerUtils.java    | 102 +++++++++++++++++++
 .../common/util/EventListenerUtilsTest.java     |  84 +++++++++++++++
 .../java/org/apache/sshd/util/BaseTest.java     |  13 ++-
 6 files changed, 210 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7f88d3a1/sshd-core/src/main/java/org/apache/sshd/common/SessionListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SessionListener.java b/sshd-core/src/main/java/org/apache/sshd/common/SessionListener.java
index 32b69b3..6bbfecc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/SessionListener.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/SessionListener.java
@@ -18,12 +18,14 @@
  */
 package org.apache.sshd.common;
 
+import java.util.EventListener;
+
 /**
  * Represents an interface receiving Session events.
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface SessionListener {
+public interface SessionListener extends EventListener {
 
     enum Event {
         KeyEstablished, Authenticated, KexCompleted

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7f88d3a1/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 13ce253..a0b16c1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -54,6 +54,7 @@ import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.common.util.BufferUtils;
 import org.apache.sshd.common.util.CloseableUtils;
+import org.apache.sshd.common.util.EventListenerUtils;
 import org.apache.sshd.common.util.Readable;
 
 import static org.apache.sshd.common.SshConstants.SSH_MSG_DEBUG;
@@ -102,12 +103,12 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
     protected final Random random;
     /** Boolean indicating if this session has been authenticated or not */
     protected boolean authed;
-    /** The name of the authenticated useer */
+    /** The name of the authenticated user */
     protected String username;
 
-    /** Session listener */
+    /** Session listeners container */
     protected final List<SessionListener> listeners = new CopyOnWriteArrayList<SessionListener>();
-
+    protected final SessionListener sessionListenerProxy;
     //
     // Key exchange support
     //
@@ -177,6 +178,7 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
         this.isServer = isServer;
         this.factoryManager = factoryManager;
         this.ioSession = ioSession;
+        sessionListenerProxy = EventListenerUtils.proxyWrapper(SessionListener.class, getClass().getClassLoader(), listeners);
         random = factoryManager.getRandomFactory().create();
         authTimeoutMs = getLongProperty(FactoryManager.AUTH_TIMEOUT, authTimeoutMs);
         authTimeoutTimestamp = System.currentTimeMillis() + authTimeoutMs;
@@ -478,9 +480,7 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
     protected void doCloseImmediately() {
         super.doCloseImmediately();
         // Fire 'close' event
-        for (SessionListener sl : listeners) {
-            sl.sessionClosed(this);
-        }
+        sessionListenerProxy.sessionClosed(this);
     }
 
     protected Service[] getServices() {
@@ -1280,12 +1280,9 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
     }
 
     protected void sendEvent(SessionListener.Event event) throws IOException {
-        for (SessionListener sl : listeners) {
-            sl.sessionEvent(this, event);
-        }
+    	sessionListenerProxy.sessionEvent(this, event);
     }
 
-
     /**
      * {@inheritDoc}
      */

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7f88d3a1/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSessionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSessionFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSessionFactory.java
index 8c8429c..5127538 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSessionFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSessionFactory.java
@@ -34,6 +34,10 @@ public abstract class AbstractSessionFactory extends AbstractSessionIoHandler {
 
     protected final List<SessionListener> listeners = new CopyOnWriteArrayList<SessionListener>();
 
+    protected AbstractSessionFactory() {
+    	super();
+    }
+
     protected AbstractSession createSession(IoSession ioSession) throws Exception {
         AbstractSession session = doCreateSession(ioSession);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7f88d3a1/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
new file mode 100644
index 0000000..f951a91
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
@@ -0,0 +1,102 @@
+/*
+ * 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.util;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.EventListener;
+
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class EventListenerUtils {
+    /**
+     * Provides proxy wrapper around an {@link Iterable} container of listener
+     * interface implementation. <b>Note:</b> a listener interface is one whose
+     * invoked methods return <u>only</u> {@code void}.
+     *
+     * @param listenerType The expected listener <u>interface</u>
+     * @param listeners    An {@link Iterable} container of listeners to be invoked.</P>
+     *                     <b>Note(s):</b>
+     *                     <ul>
+     *                     <li>The invocation order is same as the {@link Iterable} container</li>
+     *                     <p/>
+     *                     <li>If any of the invoked listener methods throws an exception, the
+     *                     rest of the listener are <u>not</u> invoked and the exception is
+     *                     propagated to the caller</li>
+     *                     <p/>
+     *                     <li>It is up to the <u>caller</u> to ensure that the container does
+     *                     not change while the proxy is invoked</li>
+     *                     </ul>
+     * @return A proxy wrapper implementing the same interface, but delegating
+     * the calls to the container
+     * @see #proxyWrapper(Class, ClassLoader, Iterable)
+     */
+    public static <T extends EventListener> T proxyWrapper(Class<T> listenerType, Iterable<? extends T> listeners) {
+        return proxyWrapper(listenerType, listenerType.getClassLoader(), listeners);
+    }
+
+    /**
+     * Provides proxy wrapper around an {@link Iterable} container of listener
+     * interface implementation. <b>Note:</b> a listener interface is one whose
+     * invoked methods return <u>only</u> {@code void}.
+     *
+     * @param listenerType The expected listener <u>interface</u>
+     * @param loader       The {@link ClassLoader} to use for the proxy
+     * @param listeners    An {@link Iterable} container of listeners to be invoked.</P>
+     *                     <b>Note(s):</b>
+     *                     <ul>
+     *                     <li>The invocation order is same as the {@link Iterable} container</li>
+     *                     <p/>
+     *                     <li>If any of the invoked listener methods throws an exception, the
+     *                     rest of the listener are <u>not</u> invoked and the exception is
+     *                     propagated to the caller</li>
+     *                     <p/>
+     *                     <li>It is up to the <u>caller</u> to ensure that the container does
+     *                     not change while the proxy is invoked</li>
+     *                     </ul>
+     * @return A proxy wrapper implementing the same interface, but delegating
+     * the calls to the container
+     * @throws IllegalArgumentException if <tt>listenerType</tt> is not an interface
+     *                                  or a {@code null} container has been provided
+     * @see #proxyWrapper(Class, ClassLoader, Iterable)
+     */
+    public static <T extends EventListener> T proxyWrapper(Class<T> listenerType, ClassLoader loader, final Iterable<? extends T> listeners) {
+        if ((listenerType == null) || (!listenerType.isInterface())) {
+            throw new IllegalArgumentException("Target proxy is not an interface");
+        }
+
+        if (listeners == null) {
+            throw new IllegalArgumentException("No listeners container provided");
+        }
+
+        Object wrapper = Proxy.newProxyInstance(loader, new Class<?>[]{listenerType}, new InvocationHandler() {
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                for (T l : listeners) {
+                    method.invoke(l, args);
+                }
+                return null;    // we assume always void return value...
+            }
+        });
+        return listenerType.cast(wrapper);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7f88d3a1/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
new file mode 100644
index 0000000..31dffaa
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.EventListener;
+import java.util.List;
+
+import org.apache.sshd.util.BaseTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class EventListenerUtilsTest extends BaseTest {
+    public EventListenerUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testProxyWrapper() {
+        List<ProxyListenerImpl> impls = new ArrayList<ProxyListenerImpl>();
+        for (int index = 0; index < Byte.SIZE; index++) {
+            impls.add(new ProxyListenerImpl());
+        }
+
+        ProxyListener listener = EventListenerUtils.proxyWrapper(ProxyListener.class, impls);
+        String expStr = getCurrentTestName();
+        Number expNum = System.currentTimeMillis();
+        listener.callMeWithString(expStr);
+        listener.callMeWithNumber(expNum);
+
+        for (int index = 0; index < impls.size(); index++) {
+            ProxyListenerImpl l = impls.get(index);
+            Assert.assertSame("Mismatched string at listener #" + index, expStr, l.getStringValue());
+            Assert.assertSame("Mismatched number at listener #" + index, expNum, l.getNumberValue());
+        }
+    }
+
+    private static interface ProxyListener extends EventListener {
+        void callMeWithString(String s);
+
+        void callMeWithNumber(Number n);
+    }
+
+    private static class ProxyListenerImpl implements ProxyListener {
+        private String strValue;
+        private Number numValue;
+
+        public String getStringValue() {
+            return strValue;
+        }
+
+        public void callMeWithString(String s) {
+            strValue = s;
+        }
+
+        public Number getNumberValue() {
+            return numValue;
+        }
+
+        public void callMeWithNumber(Number n) {
+            numValue = n;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7f88d3a1/sshd-core/src/test/java/org/apache/sshd/util/BaseTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/BaseTest.java b/sshd-core/src/test/java/org/apache/sshd/util/BaseTest.java
index d7b38c4..e81de2c 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/BaseTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/BaseTest.java
@@ -19,6 +19,7 @@
 package org.apache.sshd.util;
 
 import org.junit.Rule;
+import org.junit.rules.TestName;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 
@@ -28,11 +29,17 @@ import org.junit.runner.Description;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public abstract class BaseTest extends TestWatcher {
+    @Rule public TestWatcher rule = this;
+    @Rule public final TestName TEST_NAME_HOLDER = new TestName();
+    private long startTime;
 
-    @Rule
-    public TestWatcher rule = this;
+    protected BaseTest() {
+    	super();
+    }
 
-    private long startTime;
+    public final String getCurrentTestName() {
+        return TEST_NAME_HOLDER.getMethodName();
+    }
 
     @Override
     protected void starting(Description description) {