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 2020/12/11 08:51:25 UTC

[mina-sshd] branch master updated (975dae1 -> 1fa7425)

This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git.


    from 975dae1  Prepare for next version
     new 0bbdc77  [SSHD-1085] Added more notifications related to channel state change for detecting channel closing or closed earlier
     new daf4a18  [SSHD-1085] Added CliLogger + more verbosity on SshClientMain execution
     new 5e93306  [SSHD-1109] Route tests JUL logging via SLF4JBridgeHandler
     new b844cdf  [SSHD-1109] Provide full slf4j logger capabilities to CliLogger + use it in all CLI classes
     new a22514b  [SSHD-1109] Replace log4j with logback as the slf4j logger implementation for tests
     new 3b1b271  [SSHD-1110] Replace Class#newInstance() calls with Class#getDefaultConstructor().newInstance()
     new a0769da  Upgraded HTTP core version to 4.4.14
     new c73cb6e  Upgraded Groovy version to 3.0.7
     new 02cdfff  Upgraded Mockito version to 3.6.28
     new 88ea345  Upgraded Spring core version to 5.3.2
     new 2b129e0  Upgraded Spring integration version to 5.4.2
     new 6646f1b  Upgraded Netty version to 4.1.55.Final
     new 3362c2d  Upgraded Checkstyle version to 8.38
     new 375b17a  Upgraded Maven SCM plugin dependencies
     new 1fa7425  Upgraded OWASP Maven plugin version to 6.0.3

The 15 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGES.md                                         |   8 +
 assembly/pom.xml                                   |   4 -
 pom.xml                                            | 143 +++++++++-----
 sshd-cli/pom.xml                                   |  20 --
 .../main/java/org/apache/sshd/cli/CliLogger.java   | 205 +++++++++++++++++++++
 .../main/java/org/apache/sshd/cli/CliSupport.java  | 185 ++++---------------
 .../sshd/cli/client/CliClientModuleProperties.java |   6 +
 .../org/apache/sshd/cli/client/ScpCommandMain.java |  39 ++--
 .../apache/sshd/cli/client/SftpCommandMain.java    |  20 +-
 .../sshd/cli/client/SshClientCliSupport.java       |  30 +--
 .../org/apache/sshd/cli/client/SshClientMain.java  |  61 ++++--
 .../org/apache/sshd/cli/client/SshKeyScanMain.java |  43 +++--
 .../sshd/cli/server/SshServerCliSupport.java       |  29 +--
 .../org/apache/sshd/cli/server/SshServerMain.java  |  16 +-
 .../helper/ScpCommandTransferEventListener.java    |  37 +++-
 .../server/helper/ServerEventListenerHelper.java   |  43 +----
 .../helper/ServerPortForwardingEventListener.java  |  37 ++--
 .../helper/SftpServerSubSystemEventListener.java   |  33 ++--
 .../apache/sshd/cli/client/ChannelExecMain.java    |   4 +-
 .../org/apache/sshd/cli/server/SshFsMounter.java   |   3 +-
 sshd-cli/src/test/resources/.gitignore             |   1 +
 sshd-cli/src/test/resources/log4j.properties       |  38 ----
 sshd-common/pom.xml                                |  27 +--
 .../apache/sshd/common/util/ReflectionUtils.java   |  12 ++
 .../sshd/common/util/buffer/BufferUtils.java       |   2 +-
 .../sshd/common/util/io/NullPrintStream.java       | 172 +++++++++++++++++
 .../common/util/logging/AbstractLoggingBean.java   |  12 +-
 .../sshd/common/util/logging/LoggerSkeleton.java   | 169 +++++++++++++++++
 .../sshd/common/util/logging/LoggingUtils.java     | 128 ++++++-------
 .../sshd/common/util/logging/SimplifiedLog.java    | 135 +++++++++++++-
 .../util/logging/SimplifiedLoggerSkeleton.java     | 103 +++++++++++
 .../sshd/common/util/threads/ThreadUtils.java      |  11 +-
 .../apache/sshd/common/cipher/ARCFOUR128Test.java  |   1 +
 .../apache/sshd/common/cipher/ARCFOUR256Test.java  |   1 +
 .../apache/sshd/util/test/JUnitTestSupport.java    |  59 +++++-
 sshd-common/src/test/resources/log4j.properties    |  38 ----
 sshd-common/src/test/resources/logback-test.xml    |  47 +++++
 sshd-contrib/pom.xml                               |  20 --
 sshd-contrib/src/test/resources/log4j.properties   |  38 ----
 sshd-core/pom.xml                                  |  20 --
 .../org/apache/sshd/agent/unix/AprLibrary.java     |   2 +-
 .../sshd/client/channel/AbstractClientChannel.java |   2 +-
 .../sshd/common/channel/AbstractChannel.java       | 144 +++++++++------
 .../org/apache/sshd/common/channel/Channel.java    |  18 ++
 .../io/BuiltinIoServiceFactoryFactories.java       |   3 +-
 .../common/io/DefaultIoServiceFactoryFactory.java  |   9 +-
 .../session/helpers/AbstractConnectionService.java |  24 +--
 .../org/apache/sshd/DefaultSetupTestSupport.java   |   3 +
 .../org/apache/sshd/common/cipher/CipherTest.java  |   4 +-
 .../common/signature/OpenSSHCertificateTest.java   |   2 +
 .../common/signature/SignatureFactoriesTest.java   |   1 +
 .../org/apache/sshd/util/test/BaseTestSupport.java |  11 --
 sshd-core/src/test/resources/log4j.properties      |  38 ----
 sshd-git/pom.xml                                   |  15 --
 sshd-git/src/test/resources/log4j.properties       |  38 ----
 sshd-ldap/pom.xml                                  |  20 --
 sshd-ldap/src/test/resources/log4j.properties      |  38 ----
 sshd-mina/pom.xml                                  |  21 +--
 sshd-mina/src/test/resources/.gitignore            |   1 +
 sshd-netty/pom.xml                                 |  22 +--
 sshd-netty/src/test/resources/.gitignore           |   1 +
 sshd-openpgp/pom.xml                               |  20 --
 sshd-openpgp/src/test/resources/log4j.properties   |  38 ----
 sshd-putty/pom.xml                                 |  20 --
 sshd-putty/src/test/resources/log4j.properties     |  38 ----
 sshd-scp/pom.xml                                   |  20 --
 sshd-scp/src/main/resources/.gitignore             |   1 +
 sshd-scp/src/test/resources/.gitignore             |   1 +
 sshd-scp/src/test/resources/log4j.properties       |  38 ----
 sshd-sftp/pom.xml                                  |  20 --
 sshd-sftp/src/test/resources/.gitignore            |   1 +
 sshd-sftp/src/test/resources/log4j.properties      |  39 ----
 sshd-spring-sftp/pom.xml                           |  23 +--
 sshd-spring-sftp/src/main/resources/.gitignore     |   1 +
 sshd-spring-sftp/src/test/resources/.gitignore     |   1 +
 75 files changed, 1502 insertions(+), 1176 deletions(-)
 create mode 100644 sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
 create mode 100644 sshd-cli/src/test/resources/.gitignore
 delete mode 100644 sshd-cli/src/test/resources/log4j.properties
 create mode 100644 sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java
 create mode 100644 sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggerSkeleton.java
 create mode 100644 sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLoggerSkeleton.java
 delete mode 100644 sshd-common/src/test/resources/log4j.properties
 create mode 100644 sshd-common/src/test/resources/logback-test.xml
 delete mode 100644 sshd-contrib/src/test/resources/log4j.properties
 delete mode 100644 sshd-core/src/test/resources/log4j.properties
 delete mode 100644 sshd-git/src/test/resources/log4j.properties
 delete mode 100644 sshd-ldap/src/test/resources/log4j.properties
 create mode 100644 sshd-mina/src/test/resources/.gitignore
 create mode 100644 sshd-netty/src/test/resources/.gitignore
 delete mode 100644 sshd-openpgp/src/test/resources/log4j.properties
 delete mode 100644 sshd-putty/src/test/resources/log4j.properties
 create mode 100644 sshd-scp/src/main/resources/.gitignore
 create mode 100644 sshd-scp/src/test/resources/.gitignore
 delete mode 100644 sshd-scp/src/test/resources/log4j.properties
 create mode 100644 sshd-sftp/src/test/resources/.gitignore
 delete mode 100644 sshd-sftp/src/test/resources/log4j.properties
 create mode 100644 sshd-spring-sftp/src/main/resources/.gitignore
 create mode 100644 sshd-spring-sftp/src/test/resources/.gitignore


[mina-sshd] 10/15: Upgraded Spring core version to 5.3.2

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 88ea3452ec4bd0707ed052dfe0a46e99d28a1d02
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:35:17 2020 +0200

    Upgraded Spring core version to 5.3.2
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 03a6d5c..97b132e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,7 +107,7 @@
         <bouncycastle.version>1.67</bouncycastle.version>
         <slf4j.version>1.7.30</slf4j.version>
         <logback.version>1.2.3</logback.version>        
-        <spring.version> 5.3.1</spring.version>
+        <spring.version>5.3.2</spring.version>
         <jgit.version>5.9.0.202009080501-r</jgit.version>
         <junit.version>4.13.1</junit.version>
         <bytebuddy.version>1.10.18</bytebuddy.version>


[mina-sshd] 09/15: Upgraded Mockito version to 3.6.28

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 02cdfffabfa8f69a8fee341c7c48583e7d2a0177
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:34:43 2020 +0200

    Upgraded Mockito version to 3.6.28
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 45381c4..03a6d5c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -528,7 +528,7 @@
             <dependency>
                 <groupId>org.mockito</groupId>
                 <artifactId>mockito-core</artifactId>
-                <version>3.5.15</version>
+                <version>3.6.28</version>
             </dependency>
                 <!-- Used by mockito -->
             <dependency>


[mina-sshd] 15/15: Upgraded OWASP Maven plugin version to 6.0.3

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 1fa7425e549443360d6c50935c9dc40c24be72c8
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 22:04:04 2020 +0200

    Upgraded OWASP Maven plugin version to 6.0.3
---
 pom.xml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pom.xml b/pom.xml
index aec6ab0..c4e8df0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -176,9 +176,9 @@
                     NOTE: recommend also
 
                     * Activating 'quick' profile
-                    * Skip PMD, checkstyle
+                    * Skip PMD, checkstyle, formatting, assembly, etc.
 
-                mvn -Powasp,quick -Dpmd.skip=true -Dcheckstyle.skip=true verify
+                mvn -Powasp,quick -Danimal.sniffer.skip=true -Drat.skip=true -Dpmd.skip=true -Dcheckstyle.skip=true -Dassembly.skipAssembly=true -Dformatter.skip=true -Dimpsort.skip=true verify
 
                 -->
             <id>owasp</id>
@@ -187,7 +187,7 @@
                     <plugin>
                         <groupId>org.owasp</groupId>
                         <artifactId>dependency-check-maven</artifactId>
-                        <version>6.0.2</version>
+                        <version>6.0.3</version>
                             <!-- see https://jeremylong.github.io/DependencyCheck/dependency-check-maven/configuration.html -->
                         <configuration>
                                 <!-- see https://github.com/jeremylong/DependencyCheck/issues/1394 -->


[mina-sshd] 05/15: [SSHD-1109] Replace log4j with logback as the slf4j logger implementation for tests

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit a22514bb5a2692c7b510fbef3fdcdc86995d620d
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 12:09:41 2020 +0200

    [SSHD-1109] Replace log4j with logback as the slf4j logger implementation for tests
---
 CHANGES.md                                         |  1 +
 pom.xml                                            | 74 ++++++++++------------
 sshd-cli/src/test/resources/.gitignore             |  1 +
 sshd-cli/src/test/resources/log4j.properties       | 38 -----------
 sshd-common/pom.xml                                |  2 +
 .../sshd/common/util/logging/LoggingUtils.java     | 13 ++++
 .../apache/sshd/util/test/JUnitTestSupport.java    | 43 +++++++++++++
 sshd-common/src/test/resources/log4j.properties    | 38 -----------
 sshd-common/src/test/resources/logback-test.xml    | 47 ++++++++++++++
 sshd-contrib/src/test/resources/log4j.properties   | 38 -----------
 .../org/apache/sshd/util/test/BaseTestSupport.java | 11 ----
 sshd-core/src/test/resources/log4j.properties      | 38 -----------
 sshd-git/src/test/resources/log4j.properties       | 38 -----------
 sshd-ldap/src/test/resources/log4j.properties      | 38 -----------
 sshd-mina/src/test/resources/.gitignore            |  1 +
 sshd-netty/src/test/resources/.gitignore           |  1 +
 sshd-openpgp/src/test/resources/log4j.properties   | 38 -----------
 sshd-putty/src/test/resources/log4j.properties     | 38 -----------
 sshd-scp/src/main/resources/.gitignore             |  1 +
 sshd-scp/src/test/resources/.gitignore             |  1 +
 sshd-scp/src/test/resources/log4j.properties       | 38 -----------
 sshd-sftp/src/test/resources/.gitignore            |  1 +
 sshd-sftp/src/test/resources/log4j.properties      | 39 ------------
 sshd-spring-sftp/src/main/resources/.gitignore     |  1 +
 sshd-spring-sftp/src/test/resources/.gitignore     |  1 +
 25 files changed, 149 insertions(+), 431 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 10cf108..0da7379 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -23,3 +23,4 @@
 ## Behavioral changes and enhancements
 
 * [SSHD-1085](https://issues.apache.org/jira/browse/SSHD-1085) Added more notifications related to channel state change for detecting channel closing or closed earlier.
+* [SSHD-1109](https://issues.apache.org/jira/browse/SSHD-1109) Replace log4j with logback as the slf4j logger implementation for tests
diff --git a/pom.xml b/pom.xml
index c3fb369..e5e6ecc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,6 +106,7 @@
         <groovy.version>3.0.6</groovy.version>
         <bouncycastle.version>1.67</bouncycastle.version>
         <slf4j.version>1.7.30</slf4j.version>
+        <logback.version>1.2.3</logback.version>        
         <spring.version> 5.3.1</spring.version>
         <jgit.version>5.9.0.202009080501-r</jgit.version>
         <junit.version>4.13.1</junit.version>
@@ -449,11 +450,6 @@
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
-                <artifactId>slf4j-log4j12</artifactId>
-                <version>${slf4j.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-jdk14</artifactId>
                 <version>${slf4j.version}</version>
             </dependency>
@@ -462,6 +458,19 @@
                 <artifactId>slf4j-simple</artifactId>
                 <version>${slf4j.version}</version>
             </dependency>
+
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-core</artifactId>
+                <version>${logback.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-classic</artifactId>
+                <version>${logback.version}</version>
+            </dependency>
+           
             <dependency>
                 <groupId>com.jcraft</groupId>
                 <artifactId>jsch</artifactId>
@@ -511,37 +520,6 @@
             </dependency>
 
             <dependency>
-                <groupId>log4j</groupId>
-                <artifactId>log4j</artifactId>
-                <version>1.2.17</version>
-                <exclusions>
-                    <exclusion>
-                        <groupId>javax.jms</groupId>
-                        <artifactId>jms</artifactId>
-                    </exclusion>
-                    <exclusion>
-                        <groupId>com.sun.jmx</groupId>
-                        <artifactId>jmxri</artifactId>
-                    </exclusion>
-                    <exclusion>
-                        <groupId>com.sun.jdmk</groupId>
-                        <artifactId>jmxtools</artifactId>
-                    </exclusion>
-                    <exclusion>
-                        <groupId>ant</groupId>
-                        <artifactId>ant-nodeps</artifactId>
-                    </exclusion>
-                    <exclusion>
-                        <groupId>ant</groupId>
-                        <artifactId>ant-junit</artifactId>
-                    </exclusion>
-                    <exclusion>
-                        <groupId>ant-contrib</groupId>
-                        <artifactId>ant-contrib</artifactId>
-                    </exclusion>
-                </exclusions>
-            </dependency>
-            <dependency>
                 <groupId>junit</groupId>
                 <artifactId>junit</artifactId>
                 <version>${junit.version}</version>
@@ -627,6 +605,18 @@
                 <groupId>org.eclipse.jgit</groupId>
                 <artifactId>org.eclipse.jgit.pgm</artifactId>
                 <version>${jgit.version}</version>
+                    <!-- We provide our own slf4j logging - needed for tests only -->
+                <exclusions>
+                    <exclusion>
+                      <groupId>org.slf4j</groupId>
+                      <artifactId>slf4j-log4j12</artifactId>
+                    </exclusion>
+
+                    <exclusion>
+                      <groupId>log4j</groupId>
+                      <artifactId>log4j</artifactId>
+                    </exclusion>
+                </exclusions>
             </dependency>
 
             <dependency>
@@ -656,8 +646,13 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -1331,7 +1326,7 @@
                                 <resourceBundle>org.apache:apache-jar-resource-bundle:1.4</resourceBundle>
                             </resourceBundles>
                             <supplementalModels>
-                                <supplementalModel>${workspace.root.dir}/assembly/src/main/legal/notices.xml</supplementalModel>
+                                <supplementalModel>${workspace.root.dir}${file.separator}assembly${file.separator}src${file.separator}main${file.separator}legal${file.separator}notices.xml</supplementalModel>
                             </supplementalModels>
                             <properties>
                                 <projectName>Apache MINA SSHD</projectName>
@@ -1464,6 +1459,7 @@
                         <java.awt.headless>true</java.awt.headless>
                         <org.slf4j.simpleLogger.logFile>System.out</org.slf4j.simpleLogger.logFile>
                         <org.apache.sshd.test.timeout.factor>${sshd.tests.timeout.factor}</org.apache.sshd.test.timeout.factor>
+                        <logback.configurationFile>${workspace.root.folder}${file.separator}sshd-common${file.separator}src${file.separator}test${file.separator}resources${file.separator}logback-test.xml</logback.configurationFile>
                     </systemPropertyVariables>
                     <trimStackTrace>false</trimStackTrace>
                     <!-- lets re-run the failed test one more time, just to be sure -->
diff --git a/sshd-cli/src/test/resources/.gitignore b/sshd-cli/src/test/resources/.gitignore
new file mode 100644
index 0000000..99e3531
--- /dev/null
+++ b/sshd-cli/src/test/resources/.gitignore
@@ -0,0 +1 @@
+# Placeholder for empty folder
\ No newline at end of file
diff --git a/sshd-cli/src/test/resources/log4j.properties b/sshd-cli/src/test/resources/log4j.properties
deleted file mode 100644
index 51c6fee..0000000
--- a/sshd-cli/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-log4j.appender.logfile.file=target/sshd-cli-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-common/pom.xml b/sshd-common/pom.xml
index a1a110b..6c8cd8c 100644
--- a/sshd-common/pom.xml
+++ b/sshd-common/pom.xml
@@ -97,6 +97,8 @@
                         <configuration>
                             <includes>
                                 <include>org/apache/sshd/util/test/**/*</include>
+                                <!-- Use the same configuration for all tests logging -->
+                                <include>logback-test.xml</include>
                             </includes>
                         </configuration>
                     </execution>
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
index 263924b..13acb4d 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
@@ -25,10 +25,12 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
 import java.util.Objects;
+import java.util.Set;
 import java.util.TreeMap;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -45,11 +47,22 @@ import org.slf4j.helpers.MessageFormatter;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public final class LoggingUtils {
+    public static final Set<org.slf4j.event.Level> SLF4J_LEVELS
+            = Collections.unmodifiableSet(EnumSet.allOf(org.slf4j.event.Level.class));
 
     private LoggingUtils() {
         throw new UnsupportedOperationException("No instance");
     }
 
+    public static org.slf4j.event.Level slf4jLevelFromName(String name) {
+        return GenericUtils.isEmpty(name)
+                ? null
+                : SLF4J_LEVELS.stream()
+                        .filter(l -> name.equalsIgnoreCase(l.name()))
+                        .findAny()
+                        .orElse(null);
+    }
+
     /**
      * Scans using reflection API for all fields that are {@code public static final} that start with the given common
      * prefix (case <U>sensitive</U>) and are of type {@link Number}.
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
index a5211f0..98d870f 100644
--- a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
@@ -58,10 +58,14 @@ import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.logging.LoggingUtils;
 import org.junit.Assert;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.slf4j.bridge.SLF4JBridgeHandler;
 
 /**
@@ -78,6 +82,8 @@ public abstract class JUnitTestSupport extends Assert {
     public static final String TEST_SUBFOLDER = "test";
     public static final String RESOURCES_SUBFOLDER = "resources";
 
+    public static final org.slf4j.event.Level DEFAULT_LOGGING_LEVEL = org.slf4j.event.Level.INFO;
+
     // useful test sizes for keys
     public static final List<Integer> DSS_SIZES = Collections.unmodifiableList(Arrays.asList(512, 768, 1024));
     public static final List<Integer> RSA_SIZES = Collections.unmodifiableList(Arrays.asList(1024, 2048, 3072, 4096));
@@ -93,6 +99,43 @@ public abstract class JUnitTestSupport extends Assert {
         replaceJULLoggers();
     }
 
+    @BeforeClass
+    public static void setupRootLoggerLevel() {
+        String levelName = System.getProperty(
+                "org.apache.sshd.test.root.log.level", DEFAULT_LOGGING_LEVEL.toString());
+        org.slf4j.event.Level level = LoggingUtils.slf4jLevelFromName(levelName);
+        if (level == null) {
+            level = DEFAULT_LOGGING_LEVEL;
+        }
+
+        replaceJULLoggers();
+
+        Logger rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+        if (rootLogger instanceof ch.qos.logback.classic.Logger) {
+            Class<?> clazz = rootLogger.getClass();
+            ch.qos.logback.classic.Level rawLevel = getRawLoggerLevel(level);
+            ((ch.qos.logback.classic.Logger) rootLogger).setLevel(rawLevel);
+            rootLogger.info("Using {} logger(s) at level={}", clazz.getName(), rawLevel);
+        }
+    }
+
+    public static ch.qos.logback.classic.Level getRawLoggerLevel(org.slf4j.event.Level level) {
+        if (org.slf4j.event.Level.ERROR.equals(level)) {
+            return ch.qos.logback.classic.Level.ERROR;
+        } else if (org.slf4j.event.Level.WARN.equals(level)) {
+            return ch.qos.logback.classic.Level.WARN;
+        } else if (org.slf4j.event.Level.INFO.equals(level)) {
+            return ch.qos.logback.classic.Level.INFO;
+        } else if (org.slf4j.event.Level.DEBUG.equals(level)) {
+            return ch.qos.logback.classic.Level.DEBUG;
+        } else if (org.slf4j.event.Level.TRACE.equals(level)) {
+            return ch.qos.logback.classic.Level.TRACE;
+        } else {
+            return ch.qos.logback.classic.Level.INFO;
+        }
+
+    }
+
     public final String getCurrentTestName() {
         return testNameHolder.getMethodName();
     }
diff --git a/sshd-common/src/test/resources/log4j.properties b/sshd-common/src/test/resources/log4j.properties
deleted file mode 100644
index cf1d08a..0000000
--- a/sshd-common/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-log4j.appender.logfile.file=target/sshd-common-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-common/src/test/resources/logback-test.xml b/sshd-common/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..f6e91d8
--- /dev/null
+++ b/sshd-common/src/test/resources/logback-test.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+-->
+
+<!DOCTYPE configuration>
+<configuration debug="true">
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <!--
+            TODO see if can use a LoggerStartupListener
+            as described in https://stackoverflow.com/questions/1975939/read-environment-variables-from-logback-configuration-file 
+         -->
+        <file>target/sshd-tests.log</file>
+        <append>true</append>
+        <!-- set immediateFlush to false for much higher logging throughput -->
+        <immediateFlush>false</immediateFlush>
+        <encoder>
+            <pattern>%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n</pattern>
+        </encoder>
+    </appender>
+  
+        <!-- root level can be changed via -Dorg.apache.sshd.test.root.log.level=XXX -->
+    <root level="INFO">
+        <appender-ref ref="STDOUT" />
+        <appender-ref ref="FILE" />
+    </root>
+</configuration>
\ No newline at end of file
diff --git a/sshd-contrib/src/test/resources/log4j.properties b/sshd-contrib/src/test/resources/log4j.properties
deleted file mode 100644
index 18ea57c..0000000
--- a/sshd-contrib/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-log4j.appender.logfile.file=target/sshd-contrib-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
index 068a2d8..01c2a9b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
@@ -32,7 +32,6 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.apache.sshd.server.SshServer;
 import org.junit.Assume;
-import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
@@ -90,16 +89,6 @@ public abstract class BaseTestSupport extends JUnitTestSupport {
         super();
     }
 
-    @BeforeClass
-    public static void setupRootLoggerLevel() {
-        String levelName = System.getProperty(
-                "org.apache.sshd.test.root.log.level", org.apache.log4j.Level.INFO.toString());
-        org.apache.log4j.Level level = org.apache.log4j.Level.toLevel(
-                levelName.toUpperCase(), org.apache.log4j.Level.INFO);
-        org.apache.log4j.Logger logger = org.apache.log4j.Logger.getRootLogger();
-        logger.setLevel(level);
-    }
-
     protected SshServer setupTestServer() {
         return CoreTestSupportUtils.setupTestServer(getClass());
     }
diff --git a/sshd-core/src/test/resources/log4j.properties b/sshd-core/src/test/resources/log4j.properties
deleted file mode 100644
index 4e4dd65..0000000
--- a/sshd-core/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-log4j.appender.logfile.file=target/sshd-core-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-git/src/test/resources/log4j.properties b/sshd-git/src/test/resources/log4j.properties
deleted file mode 100644
index 87b7908..0000000
--- a/sshd-git/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-log4j.appender.logfile.file=target/sshd-git-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-ldap/src/test/resources/log4j.properties b/sshd-ldap/src/test/resources/log4j.properties
deleted file mode 100644
index 590c257..0000000
--- a/sshd-ldap/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-log4j.appender.logfile.file=target/sshd-ldap-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-mina/src/test/resources/.gitignore b/sshd-mina/src/test/resources/.gitignore
new file mode 100644
index 0000000..99e3531
--- /dev/null
+++ b/sshd-mina/src/test/resources/.gitignore
@@ -0,0 +1 @@
+# Placeholder for empty folder
\ No newline at end of file
diff --git a/sshd-netty/src/test/resources/.gitignore b/sshd-netty/src/test/resources/.gitignore
new file mode 100644
index 0000000..99e3531
--- /dev/null
+++ b/sshd-netty/src/test/resources/.gitignore
@@ -0,0 +1 @@
+# Placeholder for empty folder
\ No newline at end of file
diff --git a/sshd-openpgp/src/test/resources/log4j.properties b/sshd-openpgp/src/test/resources/log4j.properties
deleted file mode 100644
index e3f24df..0000000
--- a/sshd-openpgp/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-log4j.appender.logfile.file=target/sshd-pgp-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-putty/src/test/resources/log4j.properties b/sshd-putty/src/test/resources/log4j.properties
deleted file mode 100644
index 351467b..0000000
--- a/sshd-putty/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
-log4j.appender.logfile.file=target/sshd-putty-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-scp/src/main/resources/.gitignore b/sshd-scp/src/main/resources/.gitignore
new file mode 100644
index 0000000..99e3531
--- /dev/null
+++ b/sshd-scp/src/main/resources/.gitignore
@@ -0,0 +1 @@
+# Placeholder for empty folder
\ No newline at end of file
diff --git a/sshd-scp/src/test/resources/.gitignore b/sshd-scp/src/test/resources/.gitignore
new file mode 100644
index 0000000..99e3531
--- /dev/null
+++ b/sshd-scp/src/test/resources/.gitignore
@@ -0,0 +1 @@
+# Placeholder for empty folder
\ No newline at end of file
diff --git a/sshd-scp/src/test/resources/log4j.properties b/sshd-scp/src/test/resources/log4j.properties
deleted file mode 100644
index 0159bbd..0000000
--- a/sshd-scp/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-log4j.appender.logfile.file=target/sshd-scp-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-sftp/src/test/resources/.gitignore b/sshd-sftp/src/test/resources/.gitignore
new file mode 100644
index 0000000..31e6820
--- /dev/null
+++ b/sshd-sftp/src/test/resources/.gitignore
@@ -0,0 +1 @@
+# Placeholder for empty folder
diff --git a/sshd-sftp/src/test/resources/log4j.properties b/sshd-sftp/src/test/resources/log4j.properties
deleted file mode 100644
index 6643321..0000000
--- a/sshd-sftp/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# 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.
-#
-#
-
-#
-# The logging properties used during tests..
-#
-log4j.rootLogger=INFO, stdout, logfile
-log4j.logger.io.netty.handler.logging=ERROR
-#log4j.logger.org.apache.sshd=TRACE
-#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
-
-# CONSOLE appender
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-
-# File appender
-log4j.appender.logfile=org.apache.log4j.FileAppender
-log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
-log4j.appender.logfile.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
-log4j.appender.logfile.file=target/sshd-sftp-tests.log
-log4j.appender.logfile.append=true
diff --git a/sshd-spring-sftp/src/main/resources/.gitignore b/sshd-spring-sftp/src/main/resources/.gitignore
new file mode 100644
index 0000000..99e3531
--- /dev/null
+++ b/sshd-spring-sftp/src/main/resources/.gitignore
@@ -0,0 +1 @@
+# Placeholder for empty folder
\ No newline at end of file
diff --git a/sshd-spring-sftp/src/test/resources/.gitignore b/sshd-spring-sftp/src/test/resources/.gitignore
new file mode 100644
index 0000000..99e3531
--- /dev/null
+++ b/sshd-spring-sftp/src/test/resources/.gitignore
@@ -0,0 +1 @@
+# Placeholder for empty folder
\ No newline at end of file


[mina-sshd] 12/15: Upgraded Netty version to 4.1.55.Final

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 6646f1b805014cafe2582d834d8e14a4bbe383e9
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:37:12 2020 +0200

    Upgraded Netty version to 4.1.55.Final
---
 sshd-netty/pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sshd-netty/pom.xml b/sshd-netty/pom.xml
index ba83aed..13fa68a 100644
--- a/sshd-netty/pom.xml
+++ b/sshd-netty/pom.xml
@@ -40,7 +40,7 @@
             <dependency>
                 <groupId>io.netty</groupId>
                 <artifactId>netty-bom</artifactId>
-                <version>4.1.54.Final</version>
+                <version>4.1.55.Final</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>


[mina-sshd] 14/15: Upgraded Maven SCM plugin dependencies

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 375b17af8195c875f6b556a29b96211a6a114c0e
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:42:56 2020 +0200

    Upgraded Maven SCM plugin dependencies
---
 pom.xml | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 577567c..aec6ab0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -114,6 +114,7 @@
 
         <surefire.plugin.version>3.0.0-M5</surefire.plugin.version>
         <maven.archiver.version>3.5.0</maven.archiver.version>
+        <scm.plugin.version>1.11.2</scm.plugin.version>
         <plexus.archiver.version>4.2.3</plexus.archiver.version>
         <!-- See https://pmd.github.io/ for available latest version -->
         <pmd.version>6.29.0</pmd.version>
@@ -739,7 +740,29 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-scm-plugin</artifactId>
-                    <version>1.11.2</version>
+                    <version>${scm.plugin.version}</version>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.apache.maven.scm</groupId>
+                            <artifactId>maven-scm-api</artifactId>
+                            <version>>${scm.plugin.version}</version>
+                        </dependency>                    
+                        <dependency>
+                            <groupId>org.apache.maven.scm</groupId>
+                            <artifactId>maven-scm-provider-gitexe</artifactId>
+                            <version>>${scm.plugin.version}</version>
+                        </dependency>                    
+                        <dependency>
+                            <groupId>org.apache.maven.scm</groupId>
+                            <artifactId>maven-scm-provider-svn-commons</artifactId>
+                            <version>>${scm.plugin.version}</version>
+                        </dependency>                    
+                        <dependency>
+                            <groupId>org.apache.maven.scm</groupId>
+                            <artifactId>maven-scm-provider-svnexe</artifactId>
+                            <version>>${scm.plugin.version}</version>
+                        </dependency>                    
+                    </dependencies>
                 </plugin>                
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>


[mina-sshd] 07/15: Upgraded HTTP core version to 4.4.14

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit a0769da9ee331a5b207f27941c19d16da996dca6
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:33:27 2020 +0200

    Upgraded HTTP core version to 4.4.14
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index e5e6ecc..d8dad0b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -572,7 +572,7 @@
             <dependency>
                 <groupId>org.apache.httpcomponents</groupId>
                 <artifactId>httpcore</artifactId>
-                <version>4.4.13</version>
+                <version>4.4.14</version>
             </dependency>
             <dependency>
                 <groupId>ch.ethz.ganymed</groupId>


[mina-sshd] 02/15: [SSHD-1085] Added CliLogger + more verbosity on SshClientMain execution

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit daf4a18eca51cc951b5715b449e07c85ae035c48
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Wed Dec 9 23:07:33 2020 +0200

    [SSHD-1085] Added CliLogger + more verbosity on SshClientMain execution
---
 CHANGES.md                                         |   3 +
 .../main/java/org/apache/sshd/cli/CliLogger.java   | 149 +++++++++++++++++++++
 .../sshd/cli/client/CliClientModuleProperties.java |   6 +
 .../org/apache/sshd/cli/client/SshClientMain.java  |  48 ++++++-
 4 files changed, 202 insertions(+), 4 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index fc90adf..6a8b852 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -16,6 +16,9 @@
 
 ## Minor code helpers
 
+* [SSHD-1085](https://issues.apache.org/jira/browse/SSHD-1085) Added `CliLogger` + more verbosity on `SshClientMain`
+
+
 ## Behavioral changes and enhancements
 
 * [SSHD-1085](https://issues.apache.org/jira/browse/SSHD-1085) Added more notifications related to channel state change for detecting channel closing or closed earlier.
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
new file mode 100644
index 0000000..e6094fd
--- /dev/null
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
@@ -0,0 +1,149 @@
+/*
+ * 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.cli;
+
+import java.io.PrintStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.logging.Level;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CliLogger {
+    public static final DateFormat LOG_TIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
+
+    protected final Level threshold;
+    protected final PrintStream logStream;
+
+    public CliLogger(Level threshold, PrintStream logStream) {
+        this.threshold = threshold;
+        this.logStream = logStream;
+    }
+
+    public boolean isErrorEnabled() {
+        return isEnabledLevel(Level.SEVERE);
+    }
+
+    public void error(String msg) {
+        error(msg, null);
+    }
+
+    public void error(String msg, Throwable err) {
+        log(Level.SEVERE, msg, err);
+    }
+
+    public boolean isWarnEnabled() {
+        return isEnabledLevel(Level.WARNING);
+    }
+
+    public void warn(String msg) {
+        warn(msg, null);
+    }
+
+    public void warn(String msg, Throwable err) {
+        log(Level.WARNING, msg, err);
+    }
+
+    public boolean isInfoEnabled() {
+        return isEnabledLevel(Level.INFO);
+    }
+
+    public void info(String msg) {
+        info(msg, null);
+    }
+
+    public void info(String msg, Throwable err) {
+        log(Level.INFO, msg, err);
+    }
+
+    public boolean isDebugEnabled() {
+        return isEnabledLevel(Level.FINE);
+    }
+
+    public void debug(String msg) {
+        debug(msg, null);
+    }
+
+    public void debug(String msg, Throwable err) {
+        log(Level.FINE, msg, err);
+    }
+
+    public boolean isTraceEnabled() {
+        return isEnabledLevel(Level.FINER);
+    }
+
+    public void trace(String msg) {
+        trace(msg, null);
+    }
+
+    public void trace(String msg, Throwable err) {
+        log(Level.FINER, msg, err);
+    }
+
+    public boolean isEnabledLevel(Level level) {
+        return isLevelEnabled(level, threshold);
+    }
+
+    public void log(Level level, String msg, Throwable err) {
+        if (!isEnabledLevel(level)) {
+            return;
+        }
+
+        Date now = new Date();
+        String time;
+        synchronized (LOG_TIME_FORMATTER) {
+            time = LOG_TIME_FORMATTER.format(now);
+        }
+        logStream.append(time)
+                .append(' ').append(level.getName())
+                .append(' ').append(Thread.currentThread().getName())
+                .append(' ').append(msg)
+                .println();
+        if (err != null) {
+            err.printStackTrace(logStream);
+        }
+    }
+
+    public static boolean isErrorEnabled(Level level) {
+        return isLevelEnabled(level, Level.SEVERE);
+    }
+
+    public static boolean isWarnEnabled(Level level) {
+        return isLevelEnabled(level, Level.WARNING);
+    }
+
+    public static boolean isInfoEnabled(Level level) {
+        return isLevelEnabled(level, Level.INFO);
+    }
+
+    public static boolean isDebugEnabled(Level level) {
+        return isLevelEnabled(level, Level.FINE);
+    }
+
+    public static boolean isTraceEnabled(Level level) {
+        return isLevelEnabled(level, Level.FINER);
+    }
+
+    public static boolean isLevelEnabled(Level level, Level threshold) {
+        return (level != null) && (threshold != null) && (level.intValue() <= threshold.intValue());
+    }
+}
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/CliClientModuleProperties.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/CliClientModuleProperties.java
index 2af0c44..25bce07 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/CliClientModuleProperties.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/CliClientModuleProperties.java
@@ -41,6 +41,12 @@ public final class CliClientModuleProperties {
     public static final Property<Duration> AUTH_TIMEOUT
             = Property.duration("cli-auth-timeout", Duration.ofMinutes(2));
 
+    /**
+     * Key used to retrieve the value of the timeout for opening an EXEC or SHELL channel - in msec.
+     */
+    public static final Property<Duration> CHANNEL_OPEN_TIMEOUT
+            = Property.duration("cli-channel-open-timeout", Duration.ofSeconds(30));
+
     private CliClientModuleProperties() {
         throw new UnsupportedOperationException("No instance");
     }
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
index c71f4fa..89740ce 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
@@ -23,18 +23,22 @@ import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.nio.charset.Charset;
+import java.time.Duration;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
 import java.util.logging.Level;
 
+import org.apache.sshd.cli.CliLogger;
 import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.channel.ClientChannel;
 import org.apache.sshd.client.channel.ClientChannelEvent;
 import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.PtyChannelConfiguration;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
@@ -53,6 +57,7 @@ public class SshClientMain extends SshClientCliSupport {
 
     //////////////////////////////////////////////////////////////////////////
 
+    @SuppressWarnings("checkstyle:methodlength")
     public static void main(String[] args) throws Exception {
         PrintStream stdout = System.out;
         PrintStream stderr = System.err;
@@ -142,6 +147,8 @@ public class SshClientMain extends SshClientCliSupport {
                 return;
             }
 
+            CliLogger logger = new CliLogger(level, System.err);
+            boolean verbose = logger.isInfoEnabled();
             try (SshClient client = (SshClient) session.getFactoryManager()) {
                 /*
                  * String authSock = System.getenv(SshAgent.SSH_AUTHSOCKET_ENV_NAME); if (authSock == null && provider
@@ -152,6 +159,11 @@ public class SshClientMain extends SshClientCliSupport {
 
                 try {
                     if (socksPort >= 0) {
+                        if (verbose) {
+                            logger.info(
+                                    "Start dynamic port forwarding to " + SshdSocketAddress.LOCALHOST_NAME + ":" + socksPort);
+                        }
+
                         session.startDynamicPortForwarding(
                                 new SshdSocketAddress(SshdSocketAddress.LOCALHOST_NAME, socksPort));
                         Thread.sleep(Long.MAX_VALUE);
@@ -159,21 +171,46 @@ public class SshClientMain extends SshClientCliSupport {
                         Map<String, ?> env = resolveClientEnvironment(client);
                         PtyChannelConfiguration ptyConfig = resolveClientPtyOptions(client);
                         ClientChannel channel;
+                        String cmdValue;
                         if (GenericUtils.isEmpty(command)) {
                             channel = session.createShellChannel(ptyConfig, env);
                             ((ChannelShell) channel).setAgentForwarding(agentForward);
                             channel.setIn(new NoCloseInputStream(System.in));
+                            cmdValue = Channel.CHANNEL_SHELL;
                         } else {
-                            channel = session.createExecChannel(
-                                    String.join(" ", command).trim(), ptyConfig, env);
+                            cmdValue = String.join(" ", command).trim();
+                            channel = session.createExecChannel(cmdValue, ptyConfig, env);
+                        }
+
+                        if (logger.isDebugEnabled()) {
+                            logger.debug("PTY=" + ptyConfig + " for command=" + cmdValue);
+                            logger.debug("ENV=" + env + " for command=" + cmdValue);
                         }
 
                         try (OutputStream channelOut = new NoCloseOutputStream(System.out);
                              OutputStream channelErr = new NoCloseOutputStream(System.err)) {
                             channel.setOut(channelOut);
                             channel.setErr(channelErr);
-                            channel.open().await(); // TODO use verify and a configurable timeout
-                            channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L);
+
+                            Duration maxWait = CliClientModuleProperties.CHANNEL_OPEN_TIMEOUT.getRequired(channel);
+                            if (verbose) {
+                                logger.info("Wait " + maxWait + " for open channel for command=" + cmdValue);
+                            }
+                            channel.open().verify(maxWait);
+                            if (verbose) {
+                                logger.info("Channel opened for command=" + cmdValue);
+                            }
+
+                            Collection<ClientChannelEvent> result = channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L);
+                            if (verbose) {
+                                logger.info("command=" + cmdValue + " - waitFor result=" + result);
+                                if (result.contains(ClientChannelEvent.EXIT_SIGNAL)) {
+                                    logger.info("    " + ClientChannelEvent.EXIT_SIGNAL + "=" + channel.getExitSignal());
+                                }
+                                if (result.contains(ClientChannelEvent.EXIT_STATUS)) {
+                                    logger.info("    " + ClientChannelEvent.EXIT_STATUS + "=" + channel.getExitStatus());
+                                }
+                            }
                         } finally {
                             channel.close();
                         }
@@ -185,6 +222,9 @@ public class SshClientMain extends SshClientCliSupport {
             } finally {
                 session.close();
             }
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            throw e;
         } finally {
             if (logStream != null && logStream != stdout && logStream != stderr) {
                 logStream.close();


[mina-sshd] 08/15: Upgraded Groovy version to 3.0.7

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit c73cb6eea997307360c082392cbb3a88e626e846
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:34:04 2020 +0200

    Upgraded Groovy version to 3.0.7
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index d8dad0b..45381c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,7 +103,7 @@
         <ant.build.javac.target>${javac.target}</ant.build.javac.target>
 
         <min.required.maven.version>3.5.0</min.required.maven.version>
-        <groovy.version>3.0.6</groovy.version>
+        <groovy.version>3.0.7</groovy.version>
         <bouncycastle.version>1.67</bouncycastle.version>
         <slf4j.version>1.7.30</slf4j.version>
         <logback.version>1.2.3</logback.version>        


[mina-sshd] 03/15: [SSHD-1109] Route tests JUL logging via SLF4JBridgeHandler

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 5e933062675970408eee6d9bc41ef51c3f86ab92
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 06:51:45 2020 +0200

    [SSHD-1109] Route tests JUL logging via SLF4JBridgeHandler
---
 CHANGES.md                                         |   2 +-
 assembly/pom.xml                                   |   4 -
 pom.xml                                            |  40 ++++++
 sshd-cli/pom.xml                                   |  20 ---
 .../main/java/org/apache/sshd/cli/CliLogger.java   |  97 ++-------------
 .../org/apache/sshd/cli/client/SshKeyScanMain.java |  43 ++++---
 sshd-common/pom.xml                                |  25 ----
 .../sshd/common/util/buffer/BufferUtils.java       |   2 +-
 .../sshd/common/util/logging/LoggingUtils.java     |  56 +--------
 .../sshd/common/util/logging/SimplifiedLog.java    | 135 ++++++++++++++++++++-
 .../apache/sshd/util/test/JUnitTestSupport.java    |  16 ++-
 sshd-contrib/pom.xml                               |  20 ---
 sshd-core/pom.xml                                  |  20 ---
 sshd-git/pom.xml                                   |  15 ---
 sshd-ldap/pom.xml                                  |  20 ---
 sshd-mina/pom.xml                                  |  20 ---
 sshd-netty/pom.xml                                 |  20 ---
 sshd-openpgp/pom.xml                               |  20 ---
 sshd-putty/pom.xml                                 |  20 ---
 sshd-scp/pom.xml                                   |  20 ---
 sshd-sftp/pom.xml                                  |  20 ---
 sshd-spring-sftp/pom.xml                           |  21 ----
 22 files changed, 222 insertions(+), 434 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 6a8b852..054fcf4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -17,7 +17,7 @@
 ## Minor code helpers
 
 * [SSHD-1085](https://issues.apache.org/jira/browse/SSHD-1085) Added `CliLogger` + more verbosity on `SshClientMain`
-
+* [SSHD-1109](https://issues.apache.org/jira/browse/SSHD-1109) Route tests JUL logging via SLF4JBridgeHandler
 
 ## Behavioral changes and enhancements
 
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 7d28953..f1ded23 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -126,10 +126,6 @@
             <artifactId>eddsa</artifactId>
         </dependency>
             <!-- Replacement of commons-logging for Spring parts that still use it -->
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-        </dependency>
             <!-- Used for Unix sockets proxy -->
         <dependency>
             <groupId>tomcat</groupId>
diff --git a/pom.xml b/pom.xml
index 7256aba..c3fb369 100644
--- a/pom.xml
+++ b/pom.xml
@@ -444,6 +444,11 @@
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
+                <artifactId>jul-to-slf4j</artifactId>
+                <version>${slf4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
                 <artifactId>slf4j-log4j12</artifactId>
                 <version>${slf4j.version}</version>
             </dependency>
@@ -632,6 +637,41 @@
         </dependencies>
     </dependencyManagement>
 
+        <!-- Common dependencies for all projects -->
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+            <!-- Just in case any of our dependencies uses JCL -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+        </dependency>
+
+            <!-- Test dependencies -->
+       <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jul-to-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+     </dependencies>
+
     <build>
         <pluginManagement>
             <plugins>
diff --git a/sshd-cli/pom.xml b/sshd-cli/pom.xml
index 61fbf82..c5b890c 100644
--- a/sshd-cli/pom.xml
+++ b/sshd-cli/pom.xml
@@ -86,26 +86,6 @@
         </dependency>
 
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.jcraft</groupId>
             <artifactId>jsch</artifactId>
             <scope>test</scope>
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
index e6094fd..904bde8 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
@@ -23,12 +23,15 @@ import java.io.PrintStream;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Objects;
 import java.util.logging.Level;
 
+import org.apache.sshd.common.util.logging.SimplifiedLog;
+
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class CliLogger {
+public class CliLogger implements SimplifiedLog {
     public static final DateFormat LOG_TIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
 
     protected final Level threshold;
@@ -39,71 +42,13 @@ public class CliLogger {
         this.logStream = logStream;
     }
 
-    public boolean isErrorEnabled() {
-        return isEnabledLevel(Level.SEVERE);
-    }
-
-    public void error(String msg) {
-        error(msg, null);
-    }
-
-    public void error(String msg, Throwable err) {
-        log(Level.SEVERE, msg, err);
-    }
-
-    public boolean isWarnEnabled() {
-        return isEnabledLevel(Level.WARNING);
-    }
-
-    public void warn(String msg) {
-        warn(msg, null);
-    }
-
-    public void warn(String msg, Throwable err) {
-        log(Level.WARNING, msg, err);
-    }
-
-    public boolean isInfoEnabled() {
-        return isEnabledLevel(Level.INFO);
-    }
-
-    public void info(String msg) {
-        info(msg, null);
-    }
-
-    public void info(String msg, Throwable err) {
-        log(Level.INFO, msg, err);
-    }
-
-    public boolean isDebugEnabled() {
-        return isEnabledLevel(Level.FINE);
-    }
-
-    public void debug(String msg) {
-        debug(msg, null);
-    }
-
-    public void debug(String msg, Throwable err) {
-        log(Level.FINE, msg, err);
-    }
-
-    public boolean isTraceEnabled() {
-        return isEnabledLevel(Level.FINER);
-    }
-
-    public void trace(String msg) {
-        trace(msg, null);
-    }
-
-    public void trace(String msg, Throwable err) {
-        log(Level.FINER, msg, err);
-    }
-
+    @Override
     public boolean isEnabledLevel(Level level) {
-        return isLevelEnabled(level, threshold);
+        return SimplifiedLog.isLoggable(level, threshold);
     }
 
-    public void log(Level level, String msg, Throwable err) {
+    @Override
+    public void log(Level level, Object msg, Throwable err) {
         if (!isEnabledLevel(level)) {
             return;
         }
@@ -116,34 +61,10 @@ public class CliLogger {
         logStream.append(time)
                 .append(' ').append(level.getName())
                 .append(' ').append(Thread.currentThread().getName())
-                .append(' ').append(msg)
+                .append(' ').append(Objects.toString(msg))
                 .println();
         if (err != null) {
             err.printStackTrace(logStream);
         }
     }
-
-    public static boolean isErrorEnabled(Level level) {
-        return isLevelEnabled(level, Level.SEVERE);
-    }
-
-    public static boolean isWarnEnabled(Level level) {
-        return isLevelEnabled(level, Level.WARNING);
-    }
-
-    public static boolean isInfoEnabled(Level level) {
-        return isLevelEnabled(level, Level.INFO);
-    }
-
-    public static boolean isDebugEnabled(Level level) {
-        return isLevelEnabled(level, Level.FINE);
-    }
-
-    public static boolean isTraceEnabled(Level level) {
-        return isLevelEnabled(level, Level.FINER);
-    }
-
-    public static boolean isLevelEnabled(Level level, Level threshold) {
-        return (level != null) && (threshold != null) && (level.intValue() <= threshold.intValue());
-    }
 }
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
index 4e9513d..d9f7f45 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
@@ -81,7 +81,6 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.logging.LoggingUtils;
 import org.apache.sshd.common.util.logging.SimplifiedLog;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 import org.apache.sshd.common.util.security.SecurityUtils;
@@ -158,7 +157,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
 
     @Override
     public void log(Level level, Object message, Throwable t) {
-        if (isEnabled(level)) {
+        if (isEnabledLevel(level)) {
             PrintStream ps = System.out;
             if ((t != null) || Level.SEVERE.equals(level) || Level.WARNING.equals(level)) {
                 ps = System.err;
@@ -172,8 +171,8 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
     }
 
     @Override
-    public boolean isEnabled(Level level) {
-        return LoggingUtils.isLoggable(level, getLogLevel());
+    public boolean isEnabledLevel(Level level) {
+        return SimplifiedLog.isLoggable(level, getLogLevel());
     }
 
     @Override
@@ -191,7 +190,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
         for (String kt : sigTypes) {
             List<NamedFactory<Signature>> factories = resolveSignatureFactories(kt);
             if (GenericUtils.isEmpty(factories)) {
-                if (isEnabled(Level.FINEST)) {
+                if (isEnabledLevel(Level.FINEST)) {
                     log(Level.FINEST, "Skip empty signature factories for " + kt);
                 }
                 pairsMap.remove(kt);
@@ -230,7 +229,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
 
                     @Override
                     public void serverVersionInfo(ClientSession session, List<String> lines) {
-                        if (isEnabled(Level.FINE) && GenericUtils.isNotEmpty(lines)) {
+                        if (isEnabledLevel(Level.FINE) && GenericUtils.isNotEmpty(lines)) {
                             for (String l : lines) {
                                 log(Level.FINE, "Server Info: " + l);
                             }
@@ -239,7 +238,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
 
                     @Override
                     public void welcome(ClientSession session, String banner, String lang) {
-                        if (isEnabled(Level.FINE) && GenericUtils.isNotEmpty(banner)) {
+                        if (isEnabledLevel(Level.FINE) && GenericUtils.isNotEmpty(banner)) {
                             String[] lines = GenericUtils.split(banner, '\n');
                             for (String l : lines) {
                                 log(Level.FINE, "Welcome[" + lang + "]: " + l);
@@ -270,7 +269,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
                                 throw e;
                             }
 
-                            if (isEnabled(Level.FINE)) {
+                            if (isEnabledLevel(Level.FINE)) {
                                 log(Level.FINE, "Failed to retrieve keys from " + h, e);
                             }
                             err = GenericUtils.accumulateException(err, e);
@@ -316,7 +315,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
                 client.setSignatureFactories(forced);
                 resolveServerKeys(client, host, kt, pe.getValue());
             } catch (Exception e) {
-                if (isEnabled(Level.FINE)) {
+                if (isEnabledLevel(Level.FINE)) {
                     log(Level.FINE, "Failed to resolve key=" + kt + " for " + host);
                 }
 
@@ -331,7 +330,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
 
     protected void resolveServerKeys(SshClient client, String host, String kt, List<KeyPair> ids) throws Exception {
         int connectPort = getPort();
-        if (isEnabled(Level.FINE)) {
+        if (isEnabledLevel(Level.FINE)) {
             log(Level.FINE, "Connecting to " + host + ":" + connectPort + " to retrieve key type=" + kt);
         }
 
@@ -348,13 +347,13 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
             IoSession ioSession = session.getIoSession();
             SocketAddress remoteAddress = ioSession.getRemoteAddress();
             String remoteLocation = toString(remoteAddress);
-            if (isEnabled(Level.FINE)) {
+            if (isEnabledLevel(Level.FINE)) {
                 log(Level.FINE, "Connected to " + remoteLocation + " to retrieve key type=" + kt);
             }
 
             try {
                 session.addSessionListener(this);
-                if (isEnabled(Level.FINER)) {
+                if (isEnabledLevel(Level.FINER)) {
                     log(Level.FINER, "Authenticating with key type=" + kt + " to " + remoteLocation);
                 }
 
@@ -365,7 +364,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
                     session.auth().verify(waitTime);
                     log(Level.WARNING, "Unexpected authentication success using key type=" + kt + " with " + remoteLocation);
                 } catch (Exception e) {
-                    if (isEnabled(Level.FINER)) {
+                    if (isEnabledLevel(Level.FINER)) {
                         log(Level.FINER, "Failed to authenticate using key type=" + kt + " with " + remoteLocation);
                     }
                 } finally {
@@ -385,7 +384,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
     @Override
     public void sessionEvent(Session session, Event event) {
         logSessionEvent(session, event);
-        if (isEnabled(Level.FINEST) && Event.KexCompleted.equals(event)) {
+        if (isEnabledLevel(Level.FINEST) && Event.KexCompleted.equals(event)) {
             IoSession ioSession = session.getIoSession();
             SocketAddress remoteAddress = ioSession.getRemoteAddress();
             String remoteLocation = toString(remoteAddress);
@@ -415,7 +414,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
     }
 
     protected void logNegotiationProposal(String type, Map<KexProposalOption, String> proposal) {
-        if (!isEnabled(Level.FINEST)) {
+        if (!isEnabledLevel(Level.FINEST)) {
             return;
         }
 
@@ -435,7 +434,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
     }
 
     protected void logSessionEvent(Session session, Object event) {
-        if (isEnabled(Level.FINEST)) {
+        if (isEnabledLevel(Level.FINEST)) {
             IoSession ioSession = session.getIoSession();
             SocketAddress remoteAddress = ioSession.getRemoteAddress();
             log(Level.FINEST, "Session " + toString(remoteAddress) + " event: " + event);
@@ -450,11 +449,11 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
             String keyType = KeyUtils.getKeyType(serverKey);
             String current = GenericUtils.isEmpty(keyType) ? null : currentHostFingerprints.get(keyType);
             if (Objects.equals(current, extra)) {
-                if (isEnabled(Level.FINER)) {
+                if (isEnabledLevel(Level.FINER)) {
                     log(Level.FINER, "verifyServerKey(" + remoteLocation + ")[" + keyType + "] skip existing key: " + extra);
                 }
             } else {
-                if (isEnabled(Level.FINE)) {
+                if (isEnabledLevel(Level.FINE)) {
                     log(Level.FINE, "verifyServerKey(" + remoteLocation + ")[" + keyType + "] found new key: " + extra);
                 }
 
@@ -490,7 +489,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
     }
 
     protected List<NamedFactory<Signature>> resolveSignatureFactories(String keyType) throws GeneralSecurityException {
-        if (isEnabled(Level.FINE)) {
+        if (isEnabledLevel(Level.FINE)) {
             log(Level.FINE, "Resolve signature factories for " + keyType);
         }
 
@@ -501,7 +500,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
         } else if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyType)) {
             List<NamedFactory<Signature>> factories = new ArrayList<>(ECCurves.NAMES.size());
             for (String n : ECCurves.NAMES) {
-                if (isEnabled(Level.FINER)) {
+                if (isEnabledLevel(Level.FINER)) {
                     log(Level.FINER, "Resolve signature factory for curve=" + n);
                 }
 
@@ -549,7 +548,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
     }
 
     protected List<KeyPair> createKeyPairs(String keyType) throws GeneralSecurityException {
-        if (isEnabled(Level.FINE)) {
+        if (isEnabledLevel(Level.FINE)) {
             log(Level.FINE, "Generate key pairs for " + keyType);
         }
 
@@ -565,7 +564,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
             List<KeyPair> kps = new ArrayList<>(ECCurves.NAMES.size());
             for (ECCurves curve : ECCurves.VALUES) {
                 String curveName = curve.getName();
-                if (isEnabled(Level.FINER)) {
+                if (isEnabledLevel(Level.FINER)) {
                     log(Level.FINER, "Generate key pair for curve=" + curveName);
                 }
 
diff --git a/sshd-common/pom.xml b/sshd-common/pom.xml
index 42e46af..a1a110b 100644
--- a/sshd-common/pom.xml
+++ b/sshd-common/pom.xml
@@ -37,11 +37,6 @@
 
     <dependencies>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-
-        <dependency>
             <groupId>org.bouncycastle</groupId>
             <artifactId>bcpg-jdk15on</artifactId>
             <optional>true</optional>
@@ -61,26 +56,6 @@
 
             <!-- test dependencies -->
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.apache.servicemix.bundles</groupId>
             <artifactId>org.apache.servicemix.bundles.not-yet-commons-ssl</artifactId>
             <scope>test</scope>
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
index ad67a5f..8c935d1 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
@@ -86,7 +86,7 @@ public final class BufferUtils {
     public static void dumpHex(
             SimplifiedLog logger, Level level, String prefix, char sep,
             int chunkSize, byte[] data, int offset, int len) {
-        if ((logger == null) || (level == null) || (!logger.isEnabled(level))) {
+        if ((logger == null) || (level == null) || (!logger.isEnabledLevel(level))) {
             return;
         }
 
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
index 19aa8e9..3d592b5 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
@@ -190,71 +190,21 @@ public final class LoggingUtils {
         });
     }
 
-    /**
-     * Verifies if the given level is above the required threshold for logging.
-     *
-     * @param  level     The {@link Level} to evaluate
-     * @param  threshold The threshold {@link Level}
-     * @return           {@code true} if the evaluated level is above the required threshold.
-     *                   <P>
-     *                   <B>Note(s):</B>
-     *                   </P>
-     *                   <UL>
-     *                   <LI>
-     *                   <P>
-     *                   If either argument is {@code null} then result is {@code false}.
-     *                   </P>
-     *                   </LI>
-     *
-     *                   <LI>
-     *                   <P>
-     *                   If the evaluated level is {@link Level#OFF} then result is {@code false} regardless of the
-     *                   threshold.
-     *                   </P>
-     *                   </LI>
-     *
-     *                   <LI>
-     *                   <P>
-     *                   If the threshold is {@link Level#ALL} and the evaluated level is <U>not</U> {@link Level#OFF}
-     *                   the result is {@code true}.
-     *                   </P>
-     *                   </LI>
-     *
-     *                   <LI>
-     *                   <P>
-     *                   Otherwise, the evaluated level {@link Level#intValue()} must be greater or equal to the
-     *                   threshold.
-     *                   </P>
-     *                   </LI>
-     *                   </UL>
-     */
-    public static boolean isLoggable(Level level, Level threshold) {
-        if ((level == null) || (threshold == null)) {
-            return false;
-        } else if (Level.OFF.equals(level) || Level.OFF.equals(threshold)) {
-            return false;
-        } else if (Level.ALL.equals(threshold)) {
-            return true;
-        } else {
-            return level.intValue() >= threshold.intValue();
-        }
-    }
-
-    public static SimplifiedLog wrap(final Logger logger) {
+    public static SimplifiedLog wrap(Logger logger) {
         if (logger == null) {
             return SimplifiedLog.EMPTY;
         } else {
             return new SimplifiedLog() {
                 @Override
                 public void log(Level level, Object message, Throwable t) {
-                    if (isEnabled(level)) {
+                    if (isEnabledLevel(level)) {
                         logMessage(logger, level, message, t);
                     }
 
                 }
 
                 @Override
-                public boolean isEnabled(Level level) {
+                public boolean isEnabledLevel(Level level) {
                     return isLoggable(logger, level);
                 }
             };
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
index 58d58ee..0b5a39d 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
@@ -24,13 +24,12 @@ import java.util.logging.Level;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public interface SimplifiedLog {
-
     /**
      * An &quot;empty&quot; {@link SimplifiedLog} that does nothing
      */
     SimplifiedLog EMPTY = new SimplifiedLog() {
         @Override
-        public boolean isEnabled(Level level) {
+        public boolean isEnabledLevel(Level level) {
             return false;
         }
 
@@ -45,11 +44,141 @@ public interface SimplifiedLog {
         }
     };
 
-    boolean isEnabled(Level level);
+    default boolean isErrorEnabled() {
+        return isEnabledLevel(Level.SEVERE);
+    }
+
+    default void error(String msg) {
+        error(msg, null);
+    }
+
+    default void error(String msg, Throwable err) {
+        log(Level.SEVERE, msg, err);
+    }
+
+    default boolean isWarnEnabled() {
+        return isEnabledLevel(Level.WARNING);
+    }
+
+    default void warn(String msg) {
+        warn(msg, null);
+    }
+
+    default void warn(String msg, Throwable err) {
+        log(Level.WARNING, msg, err);
+    }
+
+    default boolean isInfoEnabled() {
+        return isEnabledLevel(Level.INFO);
+    }
+
+    default void info(String msg) {
+        info(msg, null);
+    }
+
+    default void info(String msg, Throwable err) {
+        log(Level.INFO, msg, err);
+    }
+
+    default boolean isDebugEnabled() {
+        return isEnabledLevel(Level.FINE);
+    }
+
+    default void debug(String msg) {
+        debug(msg, null);
+    }
+
+    default void debug(String msg, Throwable err) {
+        log(Level.FINE, msg, err);
+    }
+
+    default boolean isTraceEnabled() {
+        return isEnabledLevel(Level.FINER);
+    }
+
+    default void trace(String msg) {
+        trace(msg, null);
+    }
+
+    default void trace(String msg, Throwable err) {
+        log(Level.FINER, msg, err);
+    }
+
+    boolean isEnabledLevel(Level level);
 
     default void log(Level level, Object message) {
         log(level, message, null);
     }
 
     void log(Level level, Object message, Throwable t);
+
+    static boolean isErrorEnabled(Level level) {
+        return isLoggable(level, Level.SEVERE);
+    }
+
+    static boolean isWarnEnabled(Level level) {
+        return isLoggable(level, Level.WARNING);
+    }
+
+    static boolean isInfoEnabled(Level level) {
+        return isLoggable(level, Level.INFO);
+    }
+
+    static boolean isDebugEnabled(Level level) {
+        return isLoggable(level, Level.FINE);
+    }
+
+    static boolean isTraceEnabled(Level level) {
+        return isLoggable(level, Level.FINER);
+    }
+
+    /**
+     * Verifies if the given level is above the required threshold for logging.
+     *
+     * @param  level     The {@link Level} to evaluate
+     * @param  threshold The threshold {@link Level}
+     * @return           {@code true} if the evaluated level is above the required threshold.
+     *                   <P>
+     *                   <B>Note(s):</B>
+     *                   </P>
+     *                   <UL>
+     *                   <LI>
+     *                   <P>
+     *                   If either argument is {@code null} then result is {@code false}.
+     *                   </P>
+     *                   </LI>
+     *
+     *                   <LI>
+     *                   <P>
+     *                   If the evaluated level is {@link Level#OFF} then result is {@code false} regardless of the
+     *                   threshold.
+     *                   </P>
+     *                   </LI>
+     *
+     *                   <LI>
+     *                   <P>
+     *                   If the threshold is {@link Level#ALL} and the evaluated level is <U>not</U> {@link Level#OFF}
+     *                   the result is {@code true}.
+     *                   </P>
+     *                   </LI>
+     *
+     *                   <LI>
+     *                   <P>
+     *                   Otherwise, the evaluated level {@link Level#intValue()} must be greater or equal to the
+     *                   threshold.
+     *                   </P>
+     *                   </LI>
+     *                   </UL>
+     */
+    static boolean isLoggable(Level level, Level threshold) {
+        if ((level == null) || (threshold == null)) {
+            return false;
+        } else if (Level.OFF.equals(level) || Level.OFF.equals(threshold)) {
+            return false;
+        } else if (Level.ALL.equals(threshold)) {
+            return true;
+        } else {
+            return level.intValue() >= threshold.intValue();
+        }
+    }
 }
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
index a635fc9..a5211f0 100644
--- a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
@@ -62,6 +62,7 @@ import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
+import org.slf4j.bridge.SLF4JBridgeHandler;
 
 /**
  * TODO Add javadoc
@@ -89,7 +90,7 @@ public abstract class JUnitTestSupport extends Assert {
     private Path tempFolder;
 
     protected JUnitTestSupport() {
-        super();
+        replaceJULLoggers();
     }
 
     public final String getCurrentTestName() {
@@ -637,4 +638,17 @@ public abstract class JUnitTestSupport extends Assert {
             System.out.append("===[DEBUG]=== ").println(message);
         }
     }
+
+    /* ---------------------------------------------------------------------------- */
+
+    public static void replaceJULLoggers() {
+        if (!SLF4JBridgeHandler.isInstalled()) {
+            // Optionally remove existing handlers attached to j.u.l root logger
+            SLF4JBridgeHandler.removeHandlersForRootLogger();  // (since SLF4J 1.6.5)
+
+            // add SLF4JBridgeHandler to j.u.l's root logger, should be done once during
+            // the initialization phase of your application
+            SLF4JBridgeHandler.install();
+        }
+    }
 }
diff --git a/sshd-contrib/pom.xml b/sshd-contrib/pom.xml
index 6563c53..bb37464 100644
--- a/sshd-contrib/pom.xml
+++ b/sshd-contrib/pom.xml
@@ -80,26 +80,6 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
     <build>
diff --git a/sshd-core/pom.xml b/sshd-core/pom.xml
index f42c2c9..d589cef 100644
--- a/sshd-core/pom.xml
+++ b/sshd-core/pom.xml
@@ -81,16 +81,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.jcraft</groupId>
             <artifactId>jsch</artifactId>
             <scope>test</scope>
@@ -106,16 +96,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>commons-httpclient</groupId>
             <artifactId>commons-httpclient</artifactId>
             <scope>test</scope>
diff --git a/sshd-git/pom.xml b/sshd-git/pom.xml
index a675d64..6774c82 100644
--- a/sshd-git/pom.xml
+++ b/sshd-git/pom.xml
@@ -72,21 +72,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.jcraft</groupId>
             <artifactId>jsch</artifactId>
             <scope>test</scope>
diff --git a/sshd-ldap/pom.xml b/sshd-ldap/pom.xml
index d47cc31..760ee8b 100644
--- a/sshd-ldap/pom.xml
+++ b/sshd-ldap/pom.xml
@@ -114,26 +114,6 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
     <build>
diff --git a/sshd-mina/pom.xml b/sshd-mina/pom.xml
index e4d28dc..d2f0db8 100644
--- a/sshd-mina/pom.xml
+++ b/sshd-mina/pom.xml
@@ -75,16 +75,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.jcraft</groupId>
             <artifactId>jsch</artifactId>
             <scope>test</scope>
@@ -100,16 +90,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>commons-httpclient</groupId>
             <artifactId>commons-httpclient</artifactId>
             <scope>test</scope>
diff --git a/sshd-netty/pom.xml b/sshd-netty/pom.xml
index 6d9f59b..ba83aed 100644
--- a/sshd-netty/pom.xml
+++ b/sshd-netty/pom.xml
@@ -83,16 +83,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.jcraft</groupId>
             <artifactId>jsch</artifactId>
             <scope>test</scope>
@@ -108,16 +98,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>commons-httpclient</groupId>
             <artifactId>commons-httpclient</artifactId>
             <scope>test</scope>
diff --git a/sshd-openpgp/pom.xml b/sshd-openpgp/pom.xml
index ef37af9..60c838d 100644
--- a/sshd-openpgp/pom.xml
+++ b/sshd-openpgp/pom.xml
@@ -65,26 +65,6 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
     <build>
diff --git a/sshd-putty/pom.xml b/sshd-putty/pom.xml
index bd39b58..d0af818 100644
--- a/sshd-putty/pom.xml
+++ b/sshd-putty/pom.xml
@@ -68,26 +68,6 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
     <build>
diff --git a/sshd-scp/pom.xml b/sshd-scp/pom.xml
index ba4a22d..1632415 100644
--- a/sshd-scp/pom.xml
+++ b/sshd-scp/pom.xml
@@ -56,26 +56,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.jcraft</groupId>
             <artifactId>jsch</artifactId>
             <scope>test</scope>
diff --git a/sshd-sftp/pom.xml b/sshd-sftp/pom.xml
index f05786c..a331962 100644
--- a/sshd-sftp/pom.xml
+++ b/sshd-sftp/pom.xml
@@ -56,26 +56,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.jcraft</groupId>
             <artifactId>jsch</artifactId>
             <scope>test</scope>
diff --git a/sshd-spring-sftp/pom.xml b/sshd-spring-sftp/pom.xml
index 9a92374..eb37c5c 100644
--- a/sshd-spring-sftp/pom.xml
+++ b/sshd-spring-sftp/pom.xml
@@ -58,12 +58,6 @@
             <artifactId>sshd-sftp</artifactId>
             <version>${project.version}</version>
         </dependency>
-        <!-- Replacement of commons-logging for Spring parts that still use it -->
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <optional>true</optional>
-        </dependency>
 
         <dependency>
             <groupId>org.springframework.integration</groupId>
@@ -104,21 +98,6 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
     <build>


[mina-sshd] 04/15: [SSHD-1109] Provide full slf4j logger capabilities to CliLogger + use it in all CLI classes

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit b844cdf48ed3e21ba7438ae0bc99221037a78e8a
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 07:36:49 2020 +0200

    [SSHD-1109] Provide full slf4j logger capabilities to CliLogger + use it in all CLI classes
---
 CHANGES.md                                         |   1 +
 .../main/java/org/apache/sshd/cli/CliLogger.java   | 147 +++++++++++++++-
 .../main/java/org/apache/sshd/cli/CliSupport.java  | 185 ++++-----------------
 .../org/apache/sshd/cli/client/ScpCommandMain.java |  36 ++--
 .../apache/sshd/cli/client/SftpCommandMain.java    |  17 +-
 .../sshd/cli/client/SshClientCliSupport.java       |  27 +--
 .../org/apache/sshd/cli/client/SshClientMain.java  |  31 ++--
 .../sshd/cli/server/SshServerCliSupport.java       |  23 ++-
 .../org/apache/sshd/cli/server/SshServerMain.java  |  16 +-
 .../helper/ScpCommandTransferEventListener.java    |  37 +++--
 .../server/helper/ServerEventListenerHelper.java   |  43 +----
 .../helper/ServerPortForwardingEventListener.java  |  37 +++--
 .../helper/SftpServerSubSystemEventListener.java   |  33 ++--
 .../apache/sshd/cli/client/ChannelExecMain.java    |   4 +-
 .../org/apache/sshd/cli/server/SshFsMounter.java   |   3 +-
 .../sshd/common/util/io/NullPrintStream.java       | 172 +++++++++++++++++++
 .../common/util/logging/AbstractLoggingBean.java   |  12 +-
 .../sshd/common/util/logging/LoggerSkeleton.java   | 169 +++++++++++++++++++
 .../sshd/common/util/logging/LoggingUtils.java     |  19 +++
 .../util/logging/SimplifiedLoggerSkeleton.java     | 103 ++++++++++++
 sshd-mina/pom.xml                                  |   1 +
 21 files changed, 818 insertions(+), 298 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 054fcf4..10cf108 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -18,6 +18,7 @@
 
 * [SSHD-1085](https://issues.apache.org/jira/browse/SSHD-1085) Added `CliLogger` + more verbosity on `SshClientMain`
 * [SSHD-1109](https://issues.apache.org/jira/browse/SSHD-1109) Route tests JUL logging via SLF4JBridgeHandler
+* [SSHD-1109](https://issues.apache.org/jira/browse/SSHD-1109) Provide full slf4j logger capabilities to CliLogger + use it in all CLI classes
 
 ## Behavioral changes and enhancements
 
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
index 904bde8..04c2019 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliLogger.java
@@ -20,24 +20,38 @@
 package org.apache.sshd.cli;
 
 import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Objects;
 import java.util.logging.Level;
 
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.config.LogLevelValue;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.NullPrintStream;
 import org.apache.sshd.common.util.logging.SimplifiedLog;
+import org.apache.sshd.common.util.logging.SimplifiedLoggerSkeleton;
+import org.slf4j.Logger;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class CliLogger implements SimplifiedLog {
+public class CliLogger extends SimplifiedLoggerSkeleton {
     public static final DateFormat LOG_TIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
 
+    private static final long serialVersionUID = -3785762030194772776L;
+    private static final NullPrintStream NULL_PRINT_STREAM = new NullPrintStream();
+
     protected final Level threshold;
     protected final PrintStream logStream;
 
-    public CliLogger(Level threshold, PrintStream logStream) {
+    protected CliLogger(String name, Level threshold, PrintStream logStream) {
+        super(name);
+
         this.threshold = threshold;
         this.logStream = logStream;
     }
@@ -49,10 +63,16 @@ public class CliLogger implements SimplifiedLog {
 
     @Override
     public void log(Level level, Object msg, Throwable err) {
-        if (!isEnabledLevel(level)) {
-            return;
+        if (isEnabledLevel(level)) {
+            log(logStream, level, msg, err);
         }
+    }
 
+    public static void log(PrintStream logStream, Level level, Object msg) {
+        log(logStream, level, msg, null);
+    }
+
+    public static void log(PrintStream logStream, Level level, Object msg, Throwable err) {
         Date now = new Date();
         String time;
         synchronized (LOG_TIME_FORMATTER) {
@@ -63,8 +83,123 @@ public class CliLogger implements SimplifiedLog {
                 .append(' ').append(Thread.currentThread().getName())
                 .append(' ').append(Objects.toString(msg))
                 .println();
-        if (err != null) {
-            err.printStackTrace(logStream);
+        printStackTrace(logStream, err);
+    }
+
+    /**
+     * Looks for the {@link ConfigFileReaderSupport#LOG_LEVEL_CONFIG_PROP} in the options. If found, then uses it as the
+     * result. Otherwise, invokes {@link #resolveLoggingVerbosity(String...)}
+     *
+     * @param  resolver The {@code -o} options specified by the user
+     * @param  args     The command line arguments
+     * @return          The resolved verbosity level
+     */
+    public static Level resolveLoggingVerbosity(PropertyResolver resolver, String... args) {
+        String levelValue = PropertyResolverUtils.getString(
+                resolver, ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP);
+        if (GenericUtils.isEmpty(levelValue)) {
+            return resolveLoggingVerbosity(args);
         }
+
+        LogLevelValue level = LogLevelValue.fromName(levelValue);
+        if (level == null) {
+            throw new IllegalArgumentException(
+                    "Unknown " + ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP + " option value: " + levelValue);
+        }
+
+        return level.getLoggingLevel();
+    }
+
+    public static Level resolveLoggingVerbosity(String... args) {
+        return resolveLoggingVerbosity(args, GenericUtils.length(args));
+    }
+
+    public static Level resolveLoggingVerbosity(String[] args, int maxIndex) {
+        for (int index = 0; index < maxIndex; index++) {
+            String argName = args[index];
+            if ("-v".equals(argName)) {
+                return Level.INFO;
+            } else if ("-vv".equals(argName)) {
+                return Level.FINE;
+            } else if ("-vvv".equals(argName)) {
+                return Level.FINEST;
+            }
+        }
+
+        return Level.CONFIG;
+    }
+
+    public static Logger resolveSystemLogger(Class<?> clazz, Level threshold) {
+        return resolveSystemLogger(clazz.getName(), threshold);
+    }
+
+    public static Logger resolveSystemLogger(String name, Level threshold) {
+        return resolveLogger(name, threshold, System.out, System.err);
+    }
+
+    public static Logger resolveLogger(Class<?> clazz, Level threshold, PrintStream stdout, PrintStream stderr) {
+        return resolveLogger(clazz.getSimpleName(), threshold, stdout, stderr);
+    }
+
+    public static Logger resolveLogger(String name, Level threshold, PrintStream stdout, PrintStream stderr) {
+        PrintStream logStream = resolvePrintStream(threshold, stdout, stderr);
+        return getLogger(name, threshold, logStream);
+    }
+
+    public static boolean showError(PrintStream stderr, String message) {
+        stderr.append("ERROR: ").println(message);
+        return true;
+    }
+
+    public static boolean isEnabledVerbosityLogging(Level level) {
+        if ((level == null) || Level.OFF.equals(level) || Level.CONFIG.equals(level)
+                || Level.SEVERE.equals(level) || Level.WARNING.equals(level)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public static PrintStream resolvePrintStream(Level threshold, PrintStream stdout, PrintStream stderr) {
+        if (isEnabledVerbosityLogging(threshold)) {
+            return Level.INFO.equals(threshold) ? stderr : stdout;
+        } else {
+            return NULL_PRINT_STREAM;
+        }
+    }
+
+    public static <T extends Throwable> T printStackTrace(Appendable out, T reason) {
+        if ((reason == null) || (out == null)) {
+            return reason;
+        }
+
+        if (out instanceof PrintStream) {
+            reason.printStackTrace((PrintStream) out);
+        } else if (out instanceof PrintWriter) {
+            reason.printStackTrace((PrintWriter) out);
+        }
+
+        return reason;
+    }
+
+    public static Logger getSystemLogger(Class<?> clazz, Level threshold) {
+        return getSystemLogger(clazz.getName(), threshold);
+    }
+
+    public static Logger getSystemLogger(String name, Level threshold) {
+        return getLogger(name, threshold, resolveSystemPrintStream(threshold));
+    }
+
+    public static PrintStream resolveSystemPrintStream(Level threshold) {
+        return resolvePrintStream(threshold, System.out, System.err);
+    }
+
+    public static Logger getLogger(Class<?> clazz, Level threshold, PrintStream logStream) {
+        return getLogger(clazz.getSimpleName(), threshold, logStream);
+    }
+
+    public static Logger getLogger(String name, Level threshold, PrintStream logStream) {
+        return ((threshold == null) || Level.OFF.equals(threshold))
+                ? SimplifiedLoggerSkeleton.EMPTY : new CliLogger(name, threshold, logStream);
     }
 }
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
index aafce0c..f7306e7 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
@@ -20,14 +20,12 @@ package org.apache.sshd.cli;
 
 import java.io.IOException;
 import java.io.PrintStream;
-import java.io.PrintWriter;
 import java.net.SocketAddress;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.logging.Level;
 
 import org.apache.sshd.common.AttributeRepository;
@@ -44,7 +42,6 @@ import org.apache.sshd.common.compression.BuiltinCompressions;
 import org.apache.sshd.common.compression.Compression;
 import org.apache.sshd.common.config.CompressionConfigValue;
 import org.apache.sshd.common.config.ConfigFileReaderSupport;
-import org.apache.sshd.common.config.LogLevelValue;
 import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.io.BuiltinIoServiceFactoryFactories;
 import org.apache.sshd.common.io.IoAcceptor;
@@ -58,6 +55,7 @@ import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionContext;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.util.GenericUtils;
+import org.slf4j.Logger;
 
 /**
  * Provides common utilities for SSH client/server execution from the CLI
@@ -71,20 +69,6 @@ public abstract class CliSupport {
         super();
     }
 
-    public static boolean showError(PrintStream stderr, String message) {
-        stderr.append("ERROR: ").println(message);
-        return true;
-    }
-
-    public static boolean isEnabledVerbosityLogging(Level level) {
-        if ((level == null) || Level.OFF.equals(level) || Level.CONFIG.equals(level)
-                || Level.SEVERE.equals(level) || Level.WARNING.equals(level)) {
-            return false;
-        }
-
-        return true;
-    }
-
     public static <
             S extends SessionContext,
             M extends UserAuthInstance<S>, F extends UserAuthMethodFactory<S, M>,
@@ -160,39 +144,22 @@ public abstract class CliSupport {
 
         manager.setIoServiceFactoryFactory(factory.create());
 
-        if (!isEnabledVerbosityLogging(level)) {
-            return manager;
+        Logger logger = CliLogger.resolveLogger(CliSupport.class, level, stdout, stderr);
+        if (logger.isInfoEnabled()) {
+            manager.setIoServiceEventListener(createLoggingIoServiceEventListener(logger));
+            manager.addSessionListener(createLoggingSessionListener(logger));
         }
-
-        PrintStream out = Level.INFO.equals(level) ? stderr : stdout;
-        manager.setIoServiceEventListener(createLoggingIoServiceEventListener(out));
-        manager.addSessionListener(createLoggingSessionListener(out));
         return manager;
     }
 
-    public static void printStackTrace(Appendable out, Throwable reason) {
-        if ((reason == null) || (out == null)) {
-            return;
-        }
-
-        if (out instanceof PrintStream) {
-            reason.printStackTrace((PrintStream) out);
-        } else if (out instanceof PrintWriter) {
-            reason.printStackTrace((PrintWriter) out);
-        }
-    }
-
     @SuppressWarnings("checkstyle:anoninnerlength")
-    public static IoServiceEventListener createLoggingIoServiceEventListener(Appendable out) {
+    public static IoServiceEventListener createLoggingIoServiceEventListener(Logger logger) {
         return new IoServiceEventListener() {
             @Override
             public void connectionEstablished(
                     IoConnector connector, SocketAddress local, AttributeRepository context, SocketAddress remote)
                     throws IOException {
-                out.append("Connection established via ").append(Objects.toString(connector))
-                        .append("- local=").append(Objects.toString(local))
-                        .append(", remote=").append(Objects.toString(remote))
-                        .append(System.lineSeparator());
+                logger.info("Connection established via {} - local={}, remote={}", connector, local, remote);
             }
 
             @Override
@@ -200,13 +167,11 @@ public abstract class CliSupport {
                     IoConnector connector, SocketAddress local, AttributeRepository context,
                     SocketAddress remote, Throwable reason)
                     throws IOException {
-                out.append("Abort established connection ").append(Objects.toString(connector))
-                        .append(" - local=").append(Objects.toString(local))
-                        .append(", remote=").append(Objects.toString(remote))
-                        .append(": (").append(reason.getClass().getSimpleName()).append(')')
-                        .append(' ').append(reason.getMessage())
-                        .append(System.lineSeparator());
-                printStackTrace(out, reason);
+                logger.info("Abort established connection {}  - local={}, remote={}", connector, local, remote);
+                if (reason != null) {
+                    logger.warn("     {}: {}", reason.getClass().getSimpleName(), reason.getMessage());
+                    logger.error(reason.getClass().getSimpleName(), reason);
+                }
             }
 
             @Override
@@ -214,11 +179,7 @@ public abstract class CliSupport {
                     IoAcceptor acceptor, SocketAddress local,
                     SocketAddress remote, SocketAddress service)
                     throws IOException {
-                out.append("Connection accepted via ").append(Objects.toString(acceptor))
-                        .append(" - local=").append(Objects.toString(local))
-                        .append(", remote=").append(Objects.toString(remote))
-                        .append(", service=").append(Objects.toString(service))
-                        .append(System.lineSeparator());
+                logger.info("Connection accepted via {} - local={}, remote={}, service={}", acceptor, local, remote, service);
             }
 
             @Override
@@ -226,35 +187,26 @@ public abstract class CliSupport {
                     IoAcceptor acceptor, SocketAddress local, SocketAddress remote,
                     SocketAddress service, Throwable reason)
                     throws IOException {
-                out.append("Abort accepted connection ").append(Objects.toString(acceptor))
-                        .append(" - local=").append(Objects.toString(local))
-                        .append(", remote=").append(Objects.toString(remote))
-                        .append(", service=").append(Objects.toString(service))
-                        .append(": (").append(reason.getClass().getSimpleName()).append(')')
-                        .append(' ').append(reason.getMessage())
-                        .append(System.lineSeparator());
-                printStackTrace(out, reason);
+                logger.info("Abort accepted connection {} - local={}, remote={}, service={}", acceptor, local, remote, service);
+                if (reason != null) {
+                    logger.warn("     {}: {}", reason.getClass().getSimpleName(), reason.getMessage());
+                    logger.error(reason.getClass().getSimpleName(), reason);
+                }
             }
         };
     }
 
     @SuppressWarnings("checkstyle:anoninnerlength")
-    public static SessionListener createLoggingSessionListener(Appendable out) {
+    public static SessionListener createLoggingSessionListener(Logger logger) {
         return new SessionListener() {
             @Override
             public void sessionPeerIdentificationReceived(
                     Session session, String version, List<String> extraLines) {
-                try {
-                    out.append(Objects.toString(session))
-                            .append(" peer identification=").append(version)
-                            .append(System.lineSeparator());
-                    if (GenericUtils.isNotEmpty(extraLines)) {
-                        for (String l : extraLines) {
-                            out.append("    => ").append(l).append(System.lineSeparator());
-                        }
+                logger.info("{} peer identification={}", session, version);
+                if (GenericUtils.isNotEmpty(extraLines)) {
+                    for (String l : extraLines) {
+                        logger.info("    => {}", l);
                     }
-                } catch (IOException e) {
-                    // ignored
                 }
             }
 
@@ -269,90 +221,25 @@ public abstract class CliSupport {
                     return;
                 }
 
-                try {
-                    out.append(Objects.toString(session))
-                            .append(" KEX negotiation results:")
-                            .append(System.lineSeparator());
-                    for (KexProposalOption opt : KexProposalOption.VALUES) {
-                        String value = negotiatedOptions.get(opt);
-                        out.append("    ").append(opt.getDescription())
-                                .append(": ").append(value)
-                                .append(System.lineSeparator());
-                    }
-                } catch (IOException e) {
-                    // ignored
+                logger.info("{} KEX negotiation results:", session);
+                for (KexProposalOption opt : KexProposalOption.VALUES) {
+                    logger.info("    {}: {}", opt.getDescription(), negotiatedOptions.get(opt));
                 }
             }
 
             @Override
             public void sessionException(Session session, Throwable t) {
-                try {
-                    out.append(Objects.toString(session))
-                            .append(' ').append(t.getClass().getSimpleName())
-                            .append(": ").append(t.getMessage())
-                            .append(System.lineSeparator());
-                    printStackTrace(out, t);
-                } catch (IOException e) {
-                    // ignored
-                }
+                logger.error("{} {}: {}", session, t.getClass().getSimpleName(), t.getMessage());
+                logger.error(t.getClass().getSimpleName(), t);
             }
 
             @Override
             public void sessionClosed(Session session) {
-                try {
-                    out.append(Objects.toString(session))
-                            .append(" closed")
-                            .append(System.lineSeparator());
-                } catch (IOException e) {
-                    // ignored
-                }
+                logger.info("{} closed", session);
             }
         };
     }
 
-    public static Level resolveLoggingVerbosity(String... args) {
-        return resolveLoggingVerbosity(args, GenericUtils.length(args));
-    }
-
-    public static Level resolveLoggingVerbosity(String[] args, int maxIndex) {
-        for (int index = 0; index < maxIndex; index++) {
-            String argName = args[index];
-            if ("-v".equals(argName)) {
-                return Level.INFO;
-            } else if ("-vv".equals(argName)) {
-                return Level.FINE;
-            } else if ("-vvv".equals(argName)) {
-                return Level.FINEST;
-            }
-        }
-
-        return Level.CONFIG;
-    }
-
-    /**
-     * Looks for the {@link ConfigFileReaderSupport#LOG_LEVEL_CONFIG_PROP} in the options. If found, then uses it as the
-     * result. Otherwise, invokes {@link #resolveLoggingVerbosity(String...)}
-     *
-     * @param  resolver The {@code -o} options specified by the user
-     * @param  args     The command line arguments
-     * @return          The resolved verbosity level
-     */
-    public static Level resolveLoggingVerbosity(PropertyResolver resolver, String... args) {
-        String levelValue = PropertyResolverUtils.getString(
-                resolver, ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP);
-        if (GenericUtils.isEmpty(levelValue)) {
-            return resolveLoggingVerbosity(args);
-        }
-
-        LogLevelValue level = LogLevelValue.fromName(levelValue);
-        if (level == null) {
-            throw new IllegalArgumentException(
-                    "Unknown " + ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP + " option value: " + levelValue);
-        }
-
-        return level.getLoggingLevel();
-    }
-
     public static List<NamedFactory<Compression>> setupCompressions(PropertyResolver options, PrintStream stderr) {
         String argVal = PropertyResolverUtils.getString(
                 options, ConfigFileReaderSupport.COMPRESSION_PROP);
@@ -362,7 +249,7 @@ public abstract class CliSupport {
 
         NamedFactory<Compression> value = CompressionConfigValue.fromName(argVal);
         if (value == null) {
-            showError(stderr, "Unknown compression configuration value: " + argVal);
+            CliLogger.showError(stderr, "Unknown compression configuration value: " + argVal);
             return null;
         }
 
@@ -372,14 +259,14 @@ public abstract class CliSupport {
     public static List<NamedFactory<Compression>> setupCompressions(
             String argName, String argVal, List<NamedFactory<Compression>> current, PrintStream stderr) {
         if (GenericUtils.size(current) > 0) {
-            showError(stderr, argName + " option value re-specified: " + NamedResource.getNames(current));
+            CliLogger.showError(stderr, argName + " option value re-specified: " + NamedResource.getNames(current));
             return null;
         }
 
         BuiltinCompressions.ParseResult result = BuiltinCompressions.parseCompressionsList(argVal);
         Collection<? extends NamedFactory<Compression>> available = result.getParsedFactories();
         if (GenericUtils.isEmpty(available)) {
-            showError(stderr, "No known compressions in " + argVal);
+            CliLogger.showError(stderr, "No known compressions in " + argVal);
             return null;
         }
 
@@ -403,14 +290,14 @@ public abstract class CliSupport {
     public static List<NamedFactory<Mac>> setupMacs(
             String argName, String argVal, List<NamedFactory<Mac>> current, PrintStream stderr) {
         if (GenericUtils.size(current) > 0) {
-            showError(stderr, argName + " option value re-specified: " + NamedResource.getNames(current));
+            CliLogger.showError(stderr, argName + " option value re-specified: " + NamedResource.getNames(current));
             return null;
         }
 
         BuiltinMacs.ParseResult result = BuiltinMacs.parseMacsList(argVal);
         Collection<? extends NamedFactory<Mac>> available = result.getParsedFactories();
         if (GenericUtils.isEmpty(available)) {
-            showError(stderr, "No known MACs in " + argVal);
+            CliLogger.showError(stderr, "No known MACs in " + argVal);
             return null;
         }
 
@@ -435,14 +322,14 @@ public abstract class CliSupport {
     public static List<NamedFactory<Cipher>> setupCiphers(
             String argName, String argVal, List<NamedFactory<Cipher>> current, PrintStream stderr) {
         if (GenericUtils.size(current) > 0) {
-            showError(stderr, argName + " option value re-specified: " + NamedResource.getNames(current));
+            CliLogger.showError(stderr, argName + " option value re-specified: " + NamedResource.getNames(current));
             return null;
         }
 
         BuiltinCiphers.ParseResult result = BuiltinCiphers.parseCiphersList(argVal);
         Collection<? extends NamedFactory<Cipher>> available = result.getParsedFactories();
         if (GenericUtils.isEmpty(available)) {
-            showError(stderr, "WARNING: No known ciphers in " + argVal);
+            CliLogger.showError(stderr, "WARNING: No known ciphers in " + argVal);
             return null;
         }
 
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
index 3990cd9..8b14d4b 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
@@ -37,7 +37,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.logging.Level;
 
-import org.apache.sshd.cli.CliSupport;
+import org.apache.sshd.cli.CliLogger;
 import org.apache.sshd.client.ClientFactoryManager;
 import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider;
 import org.apache.sshd.client.config.hosts.HostConfigEntry;
@@ -58,6 +58,7 @@ import org.apache.sshd.scp.common.helpers.ScpAckInfo;
 import org.apache.sshd.scp.common.helpers.ScpReceiveDirCommandDetails;
 import org.apache.sshd.scp.common.helpers.ScpReceiveFileCommandDetails;
 import org.apache.sshd.scp.common.helpers.ScpTimestampCommandDetails;
+import org.slf4j.Logger;
 
 /**
  * @see    <A HREF="https://man7.org/linux/man-pages/man1/scp.1.html">SCP(1) - manual page</A>
@@ -95,7 +96,7 @@ public class ScpCommandMain extends SshClientCliSupport {
             if (isArgumentedOption(SCP_PORT_OPTION, argName) || "-creator".equals(argName)) {
                 index++;
                 if (index >= numArgs) {
-                    error = showError(stderr, "option requires an argument: " + argName);
+                    error = CliLogger.showError(stderr, "option requires an argument: " + argName);
                     break;
                 }
 
@@ -109,32 +110,32 @@ public class ScpCommandMain extends SshClientCliSupport {
                 threeWay = true;
                 effective.add(argName);
             } else if (argName.charAt(0) == '-') {
-                error = showError(stderr, "Unknown option: " + argName);
+                error = CliLogger.showError(stderr, "Unknown option: " + argName);
                 break;
             } else {
                 index++;
                 if (index >= numArgs) {
-                    error = showError(stderr, "Not enough arguments");
+                    error = CliLogger.showError(stderr, "Not enough arguments");
                     break;
                 }
 
                 ScpLocation source = new ScpLocation(argName);
                 ScpLocation target = new ScpLocation(args[index]);
                 if (index < (numArgs - 1)) {
-                    error = showError(stderr, "Unexpected extra arguments");
+                    error = CliLogger.showError(stderr, "Unexpected extra arguments");
                     break;
                 }
 
                 if (threeWay) {
                     if (source.isLocal() || target.isLocal()) {
-                        error = showError(stderr, "Both targets must be remote for the 3-way copy option");
+                        error = CliLogger.showError(stderr, "Both targets must be remote for the 3-way copy option");
                         break;
                     }
 
                     adjustRemoteTargetArguments(source, source, target, effective);
                 } else {
                     if (source.isLocal() == target.isLocal()) {
-                        error = showError(stderr, "Both targets are either remote or local");
+                        error = CliLogger.showError(stderr, "Both targets are either remote or local");
                         break;
                     }
 
@@ -176,7 +177,7 @@ public class ScpCommandMain extends SshClientCliSupport {
             if ("-creator".equals(argName)) {
                 index++;
                 if (index >= numArgs) {
-                    showError(stderr, "option requires an argument: " + argName);
+                    CliLogger.showError(stderr, "option requires an argument: " + argName);
                     return null;
                 }
 
@@ -260,6 +261,8 @@ public class ScpCommandMain extends SshClientCliSupport {
         try {
             if (!quiet) {
                 creator.setScpTransferEventListener(new ScpTransferEventListener() {
+                    private final Logger log = CliLogger.resolveLogger(ScpCommandMain.class, level, stdout, stderr);
+
                     @Override
                     public void startFolderEvent(
                             Session session, FileOperation op, Path file, Set<PosixFilePermission> perms) {
@@ -297,21 +300,14 @@ public class ScpCommandMain extends SshClientCliSupport {
                     private void logEvent(
                             String name, Session session, FileOperation op, Path file, long length,
                             Collection<PosixFilePermission> perms, Throwable thrown) {
-                        PrintStream ps = (thrown == null) ? stdout : stderr;
-                        ps.append("    ").append(name)
-                                .append('[').append(session.toString()).append(']')
-                                .append('[').append(op.name()).append(']')
-                                .append(' ').append(file.toString());
-                        if (length > 0L) {
-                            ps.append(' ').append("length=").append(Long.toString(length));
+                        if (!log.isInfoEnabled()) {
+                            return;
                         }
-                        ps.append(' ').append(String.valueOf(perms));
 
+                        log.info("{} - [{}][{}] (length={}) {} {}", name, session, op, file, length, perms);
                         if (thrown != null) {
-                            ps.append(" - ").append(thrown.getClass().getSimpleName()).append(": ")
-                                    .append(thrown.getMessage());
+                            log.error("{} -   {}: {}", name, thrown.getClass().getSimpleName(), thrown.getMessage());
                         }
-                        ps.println();
                     }
                 });
             }
@@ -452,7 +448,7 @@ public class ScpCommandMain extends SshClientCliSupport {
             int numArgs = GenericUtils.length(args);
             // see the way normalizeCommandArguments works...
             if (numArgs >= 2) {
-                level = CliSupport.resolveLoggingVerbosity(args, numArgs - 2);
+                level = CliLogger.resolveLoggingVerbosity(args, numArgs - 2);
                 logStream = resolveLoggingTargetStream(stdout, stderr, args, numArgs - 2);
                 if (logStream != null) {
                     setupLogging(level, stdout, stderr, logStream);
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
index 42a4d1f..eb560dd 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
@@ -42,7 +42,7 @@ import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 
-import org.apache.sshd.cli.CliSupport;
+import org.apache.sshd.cli.CliLogger;
 import org.apache.sshd.cli.client.helper.SftpFileTransferProgressOutputStream;
 import org.apache.sshd.client.ClientFactoryManager;
 import org.apache.sshd.client.session.ClientSession;
@@ -79,6 +79,7 @@ import org.apache.sshd.sftp.common.SftpConstants;
 import org.apache.sshd.sftp.common.SftpException;
 import org.apache.sshd.sftp.common.extensions.ParserUtils;
 import org.apache.sshd.sftp.common.extensions.openssh.StatVfsExtensionParser;
+import org.slf4j.Logger;
 
 /**
  * TODO Add javadoc
@@ -317,7 +318,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
         PrintStream stderr = System.err;
         OutputStream logStream = stderr;
         try (BufferedReader stdin = new BufferedReader(new InputStreamReader(new NoCloseInputStream(System.in)))) {
-            Level level = CliSupport.resolveLoggingVerbosity(args);
+            Level level = CliLogger.resolveLoggingVerbosity(args);
             logStream = resolveLoggingTargetStream(stdout, stderr, args);
             if (logStream != null) {
                 setupLogging(level, stdout, stderr, logStream);
@@ -336,13 +337,17 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
 
             try {
                 SftpClientFactory clientFactory = resolveSftpClientFactory(session);
-                if ((level != null) && (level.intValue() <= Level.INFO.intValue())) {
-                    stdout.append("Using factory=").println(clientFactory.getClass().getSimpleName());
+                Logger logger = (logStream != null)
+                        ? CliLogger.getLogger(SftpCommandMain.class, level,
+                                (logStream instanceof PrintStream) ? (PrintStream) logStream : new PrintStream(logStream))
+                        : CliLogger.resolveSystemLogger(SftpCommandMain.class, level);
+                if (logger.isInfoEnabled()) {
+                    logger.info("Using factory={}", clientFactory.getClass().getSimpleName());
                 }
 
                 SftpVersionSelector versionSelector = resolveVersionSelector(session);
-                if ((level != null) && (level.intValue() <= Level.INFO.intValue())) {
-                    stdout.append("Using version selector=").println(versionSelector);
+                if (logger.isInfoEnabled()) {
+                    logger.info("Using version selector={}", versionSelector);
                 }
 
                 try (SftpClient sftpClient = clientFactory.createSftpClient(session, versionSelector);
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
index 8947388..9fbd052 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
@@ -43,6 +43,7 @@ import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.util.logging.Logger;
 
+import org.apache.sshd.cli.CliLogger;
 import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.client.ClientAuthenticationManager;
 import org.apache.sshd.client.ClientBuilder;
@@ -135,7 +136,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             if (isArgumentedOption(portOption, argName)) {
                 i++;
                 if (i >= numArgs) {
-                    error = showError(stderr, "option requires an argument: " + argName);
+                    error = CliLogger.showError(stderr, "option requires an argument: " + argName);
                     break;
                 }
 
@@ -144,24 +145,24 @@ public abstract class SshClientCliSupport extends CliSupport {
 
             if (portOption.equals(argName)) {
                 if (port > 0) {
-                    error = showError(stderr, argName + " option value re-specified: " + port);
+                    error = CliLogger.showError(stderr, argName + " option value re-specified: " + port);
                     break;
                 }
 
                 port = Integer.parseInt(argVal);
                 if (port <= 0) {
-                    error = showError(stderr, "Bad option value for " + argName + ": " + port);
+                    error = CliLogger.showError(stderr, "Bad option value for " + argName + ": " + port);
                     break;
                 }
             } else if ("-J".equals(argName)) {
                 if (proxyJump != null) {
-                    error = showError(stderr, argName + " option value re-specified: " + proxyJump);
+                    error = CliLogger.showError(stderr, argName + " option value re-specified: " + proxyJump);
                     break;
                 }
                 proxyJump = argVal;
             } else if ("-w".equals(argName)) {
                 if (GenericUtils.length(password) > 0) {
-                    error = showError(stderr, argName + " option value re-specified: " + password);
+                    error = CliLogger.showError(stderr, argName + " option value re-specified: " + password);
                     break;
                 }
                 password = argVal;
@@ -190,7 +191,7 @@ public abstract class SshClientCliSupport extends CliSupport {
                 String opt = argVal;
                 int idx = opt.indexOf('=');
                 if (idx <= 0) {
-                    error = showError(stderr, "bad syntax for option: " + opt);
+                    error = CliLogger.showError(stderr, "bad syntax for option: " + opt);
                     break;
                 }
 
@@ -204,7 +205,7 @@ public abstract class SshClientCliSupport extends CliSupport {
                 }
             } else if ("-l".equals(argName)) {
                 if (login != null) {
-                    error = showError(stderr, argName + " option value re-specified: " + port);
+                    error = CliLogger.showError(stderr, argName + " option value re-specified: " + port);
                     break;
                 }
 
@@ -221,7 +222,7 @@ public abstract class SshClientCliSupport extends CliSupport {
                         login = host.substring(0, pos);
                         host = host.substring(pos + 1);
                     } else {
-                        error = showError(stderr, "Login already specified using -l option (" + login + "): " + host);
+                        error = CliLogger.showError(stderr, "Login already specified using -l option (" + login + "): " + host);
                         break;
                     }
                 }
@@ -229,7 +230,7 @@ public abstract class SshClientCliSupport extends CliSupport {
         }
 
         if ((!error) && GenericUtils.isEmpty(host)) {
-            error = showError(stderr, "Hostname not specified");
+            error = CliLogger.showError(stderr, "Hostname not specified");
         }
 
         if (error) {
@@ -461,7 +462,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             try {
                 setupSessionIdentities(client, identities, stdin, stdout, stderr);
             } catch (Throwable t) { // show but do not fail the setup - maybe a password can be used
-                showError(stderr, t.getClass().getSimpleName() + " while loading user keys: " + t.getMessage());
+                CliLogger.showError(stderr, t.getClass().getSimpleName() + " while loading user keys: " + t.getMessage());
             }
 
             setupServerKeyVerifier(client, resolver, stdin, stdout, stderr);
@@ -476,7 +477,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             }
             return client;
         } catch (Throwable t) {
-            showError(stderr, "Failed (" + t.getClass().getSimpleName() + ") to setup client: " + t.getMessage());
+            CliLogger.showError(stderr, "Failed (" + t.getClass().getSimpleName() + ") to setup client: " + t.getMessage());
             client.close();
             return null;
         }
@@ -649,7 +650,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             String argName = args[index];
             if ("-E".equals(argName)) {
                 if ((index + 1) >= maxIndex) {
-                    showError(stderr, "Missing " + argName + " option argument");
+                    CliLogger.showError(stderr, "Missing " + argName + " option argument");
                     return null;
                 }
 
@@ -662,7 +663,7 @@ public abstract class SshClientCliSupport extends CliSupport {
                     Path path = Paths.get(argVal).normalize().toAbsolutePath();
                     return Files.newOutputStream(path);
                 } catch (IOException e) {
-                    showError(stderr,
+                    CliLogger.showError(stderr,
                             "Failed (" + e.getClass().getSimpleName() + ") to open " + argVal + ": " + e.getMessage());
                     return null;
                 }
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
index 89740ce..20f86fa 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
@@ -32,7 +32,6 @@ import java.util.Map;
 import java.util.logging.Level;
 
 import org.apache.sshd.cli.CliLogger;
-import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.channel.ClientChannel;
@@ -44,6 +43,7 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
 import org.apache.sshd.common.util.io.NoCloseOutputStream;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.slf4j.Logger;
 
 /**
  * TODO Add javadoc
@@ -74,7 +74,7 @@ public class SshClientMain extends SshClientCliSupport {
             // handled by 'setupClientSession'
             if (GenericUtils.isEmpty(command) && isArgumentedOption("-p", argName)) {
                 if ((i + 1) >= numArgs) {
-                    error = showError(stderr, "option requires an argument: " + argName);
+                    error = CliLogger.showError(stderr, "option requires an argument: " + argName);
                     break;
                 }
 
@@ -89,17 +89,17 @@ public class SshClientMain extends SshClientCliSupport {
 
             if (GenericUtils.isEmpty(command) && "-D".equals(argName)) {
                 if ((i + 1) >= numArgs) {
-                    error = showError(stderr, "option requires an argument: " + argName);
+                    error = CliLogger.showError(stderr, "option requires an argument: " + argName);
                     break;
                 }
                 if (socksPort > 0) {
-                    error = showError(stderr, argName + " option value re-specified: " + socksPort);
+                    error = CliLogger.showError(stderr, argName + " option value re-specified: " + socksPort);
                     break;
                 }
 
                 socksPort = Integer.parseInt(args[++i]);
                 if (socksPort <= 0) {
-                    error = showError(stderr, "Bad option value for " + argName + ": " + socksPort);
+                    error = CliLogger.showError(stderr, "Bad option value for " + argName + ": " + socksPort);
                     break;
                 }
             } else if (GenericUtils.isEmpty(command) && "-A".equals(argName)) {
@@ -107,7 +107,7 @@ public class SshClientMain extends SshClientCliSupport {
             } else if (GenericUtils.isEmpty(command) && "-a".equals(argName)) {
                 agentForward = false;
             } else {
-                level = CliSupport.resolveLoggingVerbosity(args, i);
+                level = CliLogger.resolveLoggingVerbosity(args, i);
                 logStream = resolveLoggingTargetStream(stdout, stderr, args, i);
                 if (logStream == null) {
                     error = true;
@@ -147,7 +147,7 @@ public class SshClientMain extends SshClientCliSupport {
                 return;
             }
 
-            CliLogger logger = new CliLogger(level, System.err);
+            Logger logger = CliLogger.resolveSystemLogger(SshClientMain.class, level);
             boolean verbose = logger.isInfoEnabled();
             try (SshClient client = (SshClient) session.getFactoryManager()) {
                 /*
@@ -160,8 +160,7 @@ public class SshClientMain extends SshClientCliSupport {
                 try {
                     if (socksPort >= 0) {
                         if (verbose) {
-                            logger.info(
-                                    "Start dynamic port forwarding to " + SshdSocketAddress.LOCALHOST_NAME + ":" + socksPort);
+                            logger.info("Start dynamic port forwarding to {}:{}", SshdSocketAddress.LOCALHOST_NAME, socksPort);
                         }
 
                         session.startDynamicPortForwarding(
@@ -183,8 +182,8 @@ public class SshClientMain extends SshClientCliSupport {
                         }
 
                         if (logger.isDebugEnabled()) {
-                            logger.debug("PTY=" + ptyConfig + " for command=" + cmdValue);
-                            logger.debug("ENV=" + env + " for command=" + cmdValue);
+                            logger.debug("PTY={} for command={}", ptyConfig, cmdValue);
+                            logger.debug("ENV={} for command={}", env, cmdValue);
                         }
 
                         try (OutputStream channelOut = new NoCloseOutputStream(System.out);
@@ -194,21 +193,21 @@ public class SshClientMain extends SshClientCliSupport {
 
                             Duration maxWait = CliClientModuleProperties.CHANNEL_OPEN_TIMEOUT.getRequired(channel);
                             if (verbose) {
-                                logger.info("Wait " + maxWait + " for open channel for command=" + cmdValue);
+                                logger.info("Wait {} for open channel for command={}", maxWait, cmdValue);
                             }
                             channel.open().verify(maxWait);
                             if (verbose) {
-                                logger.info("Channel opened for command=" + cmdValue);
+                                logger.info("Channel opened for command={}", cmdValue);
                             }
 
                             Collection<ClientChannelEvent> result = channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L);
                             if (verbose) {
-                                logger.info("command=" + cmdValue + " - waitFor result=" + result);
+                                logger.info("command={} - waitFor result={}", cmdValue, result);
                                 if (result.contains(ClientChannelEvent.EXIT_SIGNAL)) {
-                                    logger.info("    " + ClientChannelEvent.EXIT_SIGNAL + "=" + channel.getExitSignal());
+                                    logger.info("    {}={}", ClientChannelEvent.EXIT_SIGNAL, channel.getExitSignal());
                                 }
                                 if (result.contains(ClientChannelEvent.EXIT_STATUS)) {
-                                    logger.info("    " + ClientChannelEvent.EXIT_STATUS + "=" + channel.getExitStatus());
+                                    logger.info("    {}={}", ClientChannelEvent.EXIT_STATUS, channel.getExitStatus());
                                 }
                             }
                         } finally {
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
index 6b3340b..0c00435 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
@@ -37,6 +37,7 @@ import java.util.logging.Level;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.apache.sshd.cli.CliLogger;
 import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.cli.server.helper.ScpCommandTransferEventListener;
 import org.apache.sshd.cli.server.helper.ServerPortForwardingEventListener;
@@ -54,6 +55,7 @@ import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.common.util.threads.ThreadUtils;
 import org.apache.sshd.core.CoreModuleProperties;
+import org.apache.sshd.scp.common.ScpTransferEventListener;
 import org.apache.sshd.scp.server.ScpCommandFactory;
 import org.apache.sshd.server.ServerFactoryManager;
 import org.apache.sshd.server.SshServer;
@@ -68,6 +70,7 @@ import org.apache.sshd.server.subsystem.SubsystemFactory;
 import org.apache.sshd.sftp.common.SftpConstants;
 import org.apache.sshd.sftp.server.SftpEventListener;
 import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.slf4j.Logger;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -155,8 +158,9 @@ public abstract class SshServerCliSupport extends CliSupport {
             SshServer server, Level level, PrintStream stdout, PrintStream stderr, PropertyResolver options) {
         ForwardingFilter forwardFilter = SshServerConfigFileReader.resolveServerForwarding(options);
         server.setForwardingFilter(forwardFilter);
-        if (isEnabledVerbosityLogging(level)) {
-            server.addPortForwardingEventListener(new ServerPortForwardingEventListener(stdout, stderr));
+        if (CliLogger.isEnabledVerbosityLogging(level)) {
+            Logger logger = CliLogger.resolveLogger(SshServerCliSupport.class, level, stdout, stderr);
+            server.addPortForwardingEventListener(new ServerPortForwardingEventListener(logger));
         }
         return forwardFilter;
     }
@@ -202,7 +206,8 @@ public abstract class SshServerCliSupport extends CliSupport {
         if (SftpConstants.SFTP_SUBSYSTEM_NAME.equalsIgnoreCase(nameList)) {
             SubsystemFactory factory = registerSubsystemFactoryListeners(
                     server, level, stdout, stderr, options, new SftpSubsystemFactory());
-            stdout.println("Using built-in SFTP subsystem");
+            PrintStream logStream = CliLogger.resolvePrintStream(level, stdout, stderr);
+            CliLogger.log(logStream, level, "Using built-in SFTP subsystem");
             return Collections.singletonList(factory);
         }
 
@@ -232,8 +237,9 @@ public abstract class SshServerCliSupport extends CliSupport {
             F factory)
             throws Exception {
         if (factory instanceof SftpSubsystemFactory) {
-            if (isEnabledVerbosityLogging(level)) {
-                SftpEventListener listener = new SftpServerSubSystemEventListener(stdout, stderr);
+            if (CliLogger.isEnabledVerbosityLogging(level)) {
+                Logger logger = CliLogger.resolveLogger(SftpEventListener.class, level, stdout, stderr);
+                SftpEventListener listener = new SftpServerSubSystemEventListener(logger);
                 ((SftpSubsystemFactory) factory).addSftpEventListener(listener);
             }
 
@@ -309,12 +315,13 @@ public abstract class SshServerCliSupport extends CliSupport {
     }
 
     public static ScpCommandFactory createScpCommandFactory(
-            Level level, Appendable stdout, Appendable stderr, ShellFactory delegateShellFactory) {
+            Level level, PrintStream stdout, PrintStream stderr, ShellFactory delegateShellFactory) {
         ScpCommandFactory.Builder scp = new ScpCommandFactory.Builder()
                 .withDelegate(ProcessShellCommandFactory.INSTANCE)
                 .withDelegateShellFactory(delegateShellFactory);
-        if (isEnabledVerbosityLogging(level)) {
-            scp.addEventListener(new ScpCommandTransferEventListener(stdout, stderr));
+        if (CliLogger.isEnabledVerbosityLogging(level)) {
+            Logger logger = CliLogger.resolveLogger(ScpTransferEventListener.class, level, stdout, stderr);
+            scp.addEventListener(new ScpCommandTransferEventListener(logger));
         }
 
         return scp.build();
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
index 27e48b6..69b417e 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
@@ -19,6 +19,7 @@
 
 package org.apache.sshd.cli.server;
 
+import java.io.PrintStream;
 import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.LinkedList;
@@ -29,6 +30,7 @@ import java.util.TreeMap;
 import java.util.logging.Level;
 import java.util.stream.Collectors;
 
+import org.apache.sshd.cli.CliLogger;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
@@ -46,6 +48,7 @@ import org.apache.sshd.server.config.keys.ServerIdentity;
 import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
 import org.apache.sshd.server.shell.ShellFactory;
 import org.apache.sshd.server.subsystem.SubsystemFactory;
+import org.slf4j.Logger;
 
 /**
  * TODO Add javadoc
@@ -158,7 +161,8 @@ public class SshServerMain extends SshServerCliSupport {
         }
 
         PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options);
-        Level level = resolveLoggingVerbosity(resolver, args);
+        Level level = CliLogger.resolveLoggingVerbosity(resolver, args);
+        Logger logger = CliLogger.resolveSystemLogger(SshServerMain.class, level);
         SshServer sshd = error
                 ? null
                 : setupIoServiceFactory(
@@ -192,7 +196,9 @@ public class SshServerMain extends SshServerCliSupport {
 
         ShellFactory shellFactory = resolveShellFactory(level, System.out, System.err, resolver);
         if (shellFactory != null) {
-            System.out.append("Using shell=").println(shellFactory.getClass().getName());
+            if (logger.isInfoEnabled()) {
+                logger.info("Using shell={}", shellFactory.getClass().getName());
+            }
             sshd.setShellFactory(shellFactory);
         }
 
@@ -204,7 +210,9 @@ public class SshServerMain extends SshServerCliSupport {
 
         List<SubsystemFactory> subsystems = resolveServerSubsystems(sshd, level, System.out, System.err, resolver);
         if (GenericUtils.isNotEmpty(subsystems)) {
-            System.out.append("Setup subsystems=").println(NamedResource.getNames(subsystems));
+            if (logger.isInfoEnabled()) {
+                logger.info("Setup subsystems={}", NamedResource.getNames(subsystems));
+            }
             sshd.setSubsystemFactories(subsystems);
         }
 
@@ -215,7 +223,7 @@ public class SshServerMain extends SshServerCliSupport {
     }
 
     private static CommandFactory setupCommandFactory(
-            SshServer sshd, Level level, Appendable stdout, Appendable stderr, ShellFactory shellFactory) {
+            SshServer sshd, Level level, PrintStream stdout, PrintStream stderr, ShellFactory shellFactory) {
         ScpCommandFactory scpFactory;
         if (shellFactory instanceof ScpCommandFactory) {
             scpFactory = (ScpCommandFactory) shellFactory;
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ScpCommandTransferEventListener.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ScpCommandTransferEventListener.java
index 85b9261..9dfed05 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ScpCommandTransferEventListener.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ScpCommandTransferEventListener.java
@@ -26,7 +26,9 @@ import java.util.Set;
 
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.scp.common.ScpTransferEventListener;
+import org.apache.sshd.scp.common.helpers.ScpAckInfo;
 import org.apache.sshd.scp.server.ScpCommandFactory;
+import org.slf4j.Logger;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -34,15 +36,17 @@ import org.apache.sshd.scp.server.ScpCommandFactory;
 public class ScpCommandTransferEventListener
         extends ServerEventListenerHelper
         implements ScpTransferEventListener {
-    public ScpCommandTransferEventListener(Appendable stdout, Appendable stderr) {
-        super(ScpCommandFactory.SCP_FACTORY_NAME, stdout, stderr);
+    public ScpCommandTransferEventListener(Logger logger) {
+        super(ScpCommandFactory.SCP_FACTORY_NAME, logger);
     }
 
     @Override
     public void startFileEvent(
             Session session, FileOperation op, Path file, long length, Set<PosixFilePermission> perms)
             throws IOException {
-        outputDebugMessage("startFileEvent(%s)[%s] len=%d, perms=%s: %s", session, op, length, perms, file);
+        if (log.isInfoEnabled()) {
+            log.info("startFileEvent({})[{}] len={}, perms={}: {}", session, op, length, perms, file);
+        }
     }
 
     @Override
@@ -50,17 +54,19 @@ public class ScpCommandTransferEventListener
             Session session, FileOperation op, Path file, long length, Set<PosixFilePermission> perms, Throwable thrown)
             throws IOException {
         if (thrown != null) {
-            outputErrorMessage("endFileEvent(%s)[%s] failed (%s) len=%d, perms=%s [%s]: %s",
+            log.error("endFileEvent({})[{}] failed ({}) len={}, perms={} [{}]: {}",
                     session, op, thrown.getClass().getSimpleName(), length, perms, file, thrown.getMessage());
-        } else {
-            outputDebugMessage("endFileEvent(%s)[%s] len=%d, perms=%s: %s", session, op, length, perms, file);
+        } else if (log.isInfoEnabled()) {
+            log.info("endFileEvent({})[{}] len={}, perms={}: {}", session, op, length, perms, file);
         }
     }
 
     @Override
     public void startFolderEvent(Session session, FileOperation op, Path file, Set<PosixFilePermission> perms)
             throws IOException {
-        outputDebugMessage("startFolderEvent(%s)[%s] perms=%s: %s", session, op, perms, file);
+        if (log.isInfoEnabled()) {
+            log.info("startFolderEvent({})[{}] perms={}: {}", session, op, perms, file);
+        }
     }
 
     @Override
@@ -68,10 +74,21 @@ public class ScpCommandTransferEventListener
             Session session, FileOperation op, Path file, Set<PosixFilePermission> perms, Throwable thrown)
             throws IOException {
         if (thrown != null) {
-            outputErrorMessage("endFolderEvent(%s)[%s] failed (%s) perms=%s [%s]: %s",
+            log.error("endFolderEvent({})[{}] failed ({}) perms={} [{}]: {}",
                     session, op, thrown.getClass().getSimpleName(), perms, file, thrown.getMessage());
-        } else {
-            outputDebugMessage("endFolderEvent(%s)[%s] lperms=%s: %s", session, op, perms, file);
+        } else if (log.isInfoEnabled()) {
+            log.info("endFolderEvent({})[{}] perms={}: {}", session, op, perms, file);
+        }
+    }
+
+    @Override
+    public void handleFileEventAckInfo(
+            Session session, FileOperation op, Path file, long length,
+            Set<PosixFilePermission> perms, ScpAckInfo ackInfo)
+            throws IOException {
+        if (log.isInfoEnabled()) {
+            log.info("handleFileEventAckInfo({})[{}] perms={}, length={}, ACK={}: {}",
+                    session, op, perms, length, ackInfo, file);
         }
     }
 }
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerEventListenerHelper.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerEventListenerHelper.java
index f3417ea..b79bccd 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerEventListenerHelper.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerEventListenerHelper.java
@@ -19,52 +19,21 @@
 
 package org.apache.sshd.cli.server.helper;
 
-import java.io.Flushable;
-import java.io.IOException;
-import java.util.Objects;
-
 import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+import org.slf4j.Logger;
 
-public abstract class ServerEventListenerHelper implements NamedResource {
+public abstract class ServerEventListenerHelper extends AbstractLoggingBean implements NamedResource {
     private final String name;
-    private final Appendable stdout;
-    private final Appendable stderr;
 
-    public ServerEventListenerHelper(String name, Appendable stdout, Appendable stderr) {
+    public ServerEventListenerHelper(String name, Logger logger) {
+        super(logger);
+
         this.name = name;
-        this.stdout = Objects.requireNonNull(stdout, "No output target");
-        this.stderr = Objects.requireNonNull(stderr, "No error target");
     }
 
     @Override
     public String getName() {
         return name;
     }
-
-    public Appendable getStdout() {
-        return stdout;
-    }
-
-    public Appendable getStderr() {
-        return stderr;
-    }
-
-    protected String outputErrorMessage(String format, Object... args) throws IOException {
-        return outputMessage(getStderr(), format, args);
-    }
-
-    protected String outputDebugMessage(String format, Object... args) throws IOException {
-        return outputMessage(getStdout(), format, args);
-    }
-
-    protected String outputMessage(Appendable out, String format, Object... args) throws IOException {
-        String message = String.format(format, args);
-        out.append(getName())
-                .append(": ").append(message)
-                .append(System.lineSeparator());
-        if (out instanceof Flushable) {
-            ((Flushable) out).flush();
-        }
-        return message;
-    }
 }
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerPortForwardingEventListener.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerPortForwardingEventListener.java
index c6afab4..cdcd976 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerPortForwardingEventListener.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerPortForwardingEventListener.java
@@ -24,10 +24,11 @@ import java.io.IOException;
 import org.apache.sshd.common.forward.PortForwardingEventListener;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.slf4j.Logger;
 
 public class ServerPortForwardingEventListener extends ServerEventListenerHelper implements PortForwardingEventListener {
-    public ServerPortForwardingEventListener(Appendable stdout, Appendable stderr) {
-        super("PORT-FWD", stdout, stderr);
+    public ServerPortForwardingEventListener(Logger logger) {
+        super("PORT-FWD", logger);
     }
 
     @Override
@@ -36,11 +37,13 @@ public class ServerPortForwardingEventListener extends ServerEventListenerHelper
             boolean localForwarding, SshdSocketAddress boundAddress, Throwable reason)
             throws IOException {
         if (reason == null) {
-            outputDebugMessage("Estalibshed explicit tunnel for session=%s: local=%s, remote=%s, bound=%s, localForward=%s",
-                    session, local, remote, boundAddress, localForwarding);
+            if (log.isInfoEnabled()) {
+                log.info("Estalibshed explicit tunnel for session={}: local={}, remote={}, bound={}, localForward={}",
+                        session, local, remote, boundAddress, localForwarding);
+            }
         } else {
-            outputErrorMessage(
-                    "Failed (%s) to establish explicit tunnel for session=%s, local=%s, remote=%s, bound=%s, localForward=%s: %s",
+            log.error(
+                    "Failed ({}) to establish explicit tunnel for session={}, local={}, remote={}, bound={}, localForward={}: {}",
                     reason.getClass().getSimpleName(), session, local, remote, boundAddress, localForwarding,
                     reason.getMessage());
         }
@@ -52,11 +55,13 @@ public class ServerPortForwardingEventListener extends ServerEventListenerHelper
             Throwable reason)
             throws IOException {
         if (reason == null) {
-            outputDebugMessage("Torn down explicit tunnel for session=%s: address=%s, remote=%s, localForward=%s",
-                    session, address, remoteAddress, localForwarding);
+            if (log.isInfoEnabled()) {
+                log.info("Torn down explicit tunnel for session={}: address={}, remote={}, localForward={}",
+                        session, address, remoteAddress, localForwarding);
+            }
         } else {
-            outputErrorMessage(
-                    "Failed (%s) to tear down explicit tunnel for session=%s, address=%s, remote=%s, localForward=%s: %s",
+            log.error(
+                    "Failed ({}) to tear down explicit tunnel for session={}, address={}, remote={}, localForward={}: {}",
                     reason.getClass().getSimpleName(), session, address, remoteAddress, localForwarding, reason.getMessage());
         }
     }
@@ -66,9 +71,11 @@ public class ServerPortForwardingEventListener extends ServerEventListenerHelper
             Session session, SshdSocketAddress local, SshdSocketAddress boundAddress, Throwable reason)
             throws IOException {
         if (reason == null) {
-            outputDebugMessage("Estalibshed dynamic tunnel for session=%s: local=%s,  bound=%s", session, local, boundAddress);
+            if (log.isInfoEnabled()) {
+                log.info("Estalibshed dynamic tunnel for session={}: local={},  bound={}", session, local, boundAddress);
+            }
         } else {
-            outputErrorMessage("Failed (%s) to establish dynamic tunnel for session=%s, bound=%s: %s",
+            log.error("Failed ({}) to establish dynamic tunnel for session={}, bound={}: {}",
                     reason.getClass().getSimpleName(), session, local, boundAddress, reason.getMessage());
         }
     }
@@ -78,9 +85,11 @@ public class ServerPortForwardingEventListener extends ServerEventListenerHelper
             Session session, SshdSocketAddress address, Throwable reason)
             throws IOException {
         if (reason == null) {
-            outputDebugMessage("Tornd down dynamic tunnel for session=%s: address=%s", session);
+            if (log.isInfoEnabled()) {
+                log.info("Torn down dynamic tunnel for session={}: address={}", session);
+            }
         } else {
-            outputErrorMessage("Failed (%s) to tear down dynamic tunnel for session=%s, address=%s: %s",
+            log.error("Failed ({}) to tear down dynamic tunnel for session={}, address={}: {}",
                     reason.getClass().getSimpleName(), session, address, reason.getMessage());
         }
     }
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/SftpServerSubSystemEventListener.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/SftpServerSubSystemEventListener.java
index 280a674..c534379 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/SftpServerSubSystemEventListener.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/SftpServerSubSystemEventListener.java
@@ -28,23 +28,28 @@ import java.util.Map;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.sftp.common.SftpConstants;
 import org.apache.sshd.sftp.server.SftpEventListener;
+import org.slf4j.Logger;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class SftpServerSubSystemEventListener extends ServerEventListenerHelper implements SftpEventListener {
-    public SftpServerSubSystemEventListener(Appendable stdout, Appendable stderr) {
-        super(SftpConstants.SFTP_SUBSYSTEM_NAME, stdout, stderr);
+    public SftpServerSubSystemEventListener(Logger logger) {
+        super(SftpConstants.SFTP_SUBSYSTEM_NAME, logger);
     }
 
     @Override
     public void initialized(ServerSession session, int version) throws IOException {
-        outputDebugMessage("Session %s initialized - version=%d", session, version);
+        if (log.isInfoEnabled()) {
+            log.info("Session {} initialized - version={}", session, version);
+        }
     }
 
     @Override
     public void destroying(ServerSession session) throws IOException {
-        outputDebugMessage("Session destroyed: %s", session);
+        if (log.isInfoEnabled()) {
+            log.info("Session destroyed: {}", session);
+        }
     }
 
     @Override
@@ -52,9 +57,11 @@ public class SftpServerSubSystemEventListener extends ServerEventListenerHelper
             ServerSession session, Path path, Map<String, ?> attrs, Throwable thrown)
             throws IOException {
         if (thrown == null) {
-            outputDebugMessage("Session %s created directory %s with attributes=%s", session, path, attrs);
+            if (log.isInfoEnabled()) {
+                log.info("Session {} created directory {} with attributes={}", session, path, attrs);
+            }
         } else {
-            outputErrorMessage("Failed (%s) to create directory %s in session %s: %s",
+            log.error("Failed ({}) to create directory {} in session {}: {}",
                     thrown.getClass().getSimpleName(), path, session, thrown.getMessage());
         }
     }
@@ -64,10 +71,12 @@ public class SftpServerSubSystemEventListener extends ServerEventListenerHelper
             ServerSession session, Path srcPath, Path dstPath, Collection<CopyOption> opts, Throwable thrown)
             throws IOException {
         if (thrown == null) {
-            outputDebugMessage("Session %s moved %s to %s with options=%s",
-                    session, srcPath, dstPath, opts);
+            if (log.isInfoEnabled()) {
+                log.info("Session {} moved {} to {} with options={}",
+                        session, srcPath, dstPath, opts);
+            }
         } else {
-            outputErrorMessage("Failed (%s) to move %s to %s using options=%s in session %s: %s",
+            log.error("Failed ({}) to move {} to {} using options={} in session {}: {}",
                     thrown.getClass().getSimpleName(), srcPath, dstPath, opts, session, thrown.getMessage());
         }
     }
@@ -75,9 +84,11 @@ public class SftpServerSubSystemEventListener extends ServerEventListenerHelper
     @Override
     public void removed(ServerSession session, Path path, boolean isDirectory, Throwable thrown) throws IOException {
         if (thrown == null) {
-            outputDebugMessage("Session %s removed %s", session, path);
+            if (log.isInfoEnabled()) {
+                log.info("Session {} removed {}", session, path);
+            }
         } else {
-            outputErrorMessage("Failed (%s) to remove %s in session %s: %s",
+            log.error("Failed ({}) to remove {} in session {}: {}",
                     thrown.getClass().getSimpleName(), path, session, thrown.getMessage());
         }
     }
diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java b/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
index 45424d4..e1cfc31 100644
--- a/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
+++ b/sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
@@ -24,7 +24,7 @@ import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.nio.charset.Charset;
 
-import org.apache.sshd.cli.CliSupport;
+import org.apache.sshd.cli.CliLogger;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.util.GenericUtils;
@@ -79,7 +79,7 @@ public class ChannelExecMain extends BaseTestSupport {
         try (BufferedReader stdin = new BufferedReader(
                 new InputStreamReader(new NoCloseInputStream(System.in), Charset.defaultCharset()))) {
             ClientSession session = SshClientCliSupport.setupClientSession("-P", stdin,
-                    CliSupport.resolveLoggingVerbosity(args), stdout, stderr, args);
+                    CliLogger.resolveLoggingVerbosity(args), stdout, stderr, args);
             if (session == null) {
                 System.err.println("usage: channelExec [-i identity] [-l login] [-P port] [-o option=value]"
                                    + " [-J proxyJump] [-w password] [-c cipherlist]  [-m maclist] [-C] hostname/user@host");
diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
index d42ff7d..d7e29b7 100644
--- a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
+++ b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
@@ -34,6 +34,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.logging.Level;
 
+import org.apache.sshd.cli.CliLogger;
 import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
@@ -297,7 +298,7 @@ public final class SshFsMounter extends SshServerCliSupport {
         }
 
         PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options);
-        Level level = resolveLoggingVerbosity(resolver, args);
+        Level level = CliLogger.resolveLoggingVerbosity(resolver, args);
         SshServer sshd = error
                 ? null : setupIoServiceFactory(
                         CoreTestSupportUtils.setupTestServer(SshFsMounter.class), resolver,
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java
new file mode 100644
index 0000000..a21c5d2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullPrintStream.java
@@ -0,0 +1,172 @@
+/*
+ * 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.io;
+
+import java.io.PrintStream;
+import java.util.Locale;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NullPrintStream extends PrintStream {
+    public NullPrintStream() {
+        super(new NullOutputStream());
+    }
+
+    @Override
+    public void write(int b) {
+        // ignored
+    }
+
+    @Override
+    public void write(byte[] buf, int off, int len) {
+        // ignored
+    }
+
+    @Override
+    public void print(boolean b) {
+        // ignored
+    }
+
+    @Override
+    public void print(char c) {
+        append(c);
+    }
+
+    @Override
+    public void print(int i) {
+        print((long) i);
+    }
+
+    @Override
+    public void print(long l) {
+        // ignored
+    }
+
+    @Override
+    public void print(float f) {
+        print((double) f);
+    }
+
+    @Override
+    public void print(double d) {
+        // ignored
+    }
+
+    @Override
+    public void print(char[] s) {
+        // ignored
+    }
+
+    @Override
+    public void print(String s) {
+        // ignored
+    }
+
+    @Override
+    public void print(Object obj) {
+        // ignored
+    }
+
+    @Override
+    public void println() {
+        // ignored
+    }
+
+    @Override
+    public void println(boolean x) {
+        // ignored
+    }
+
+    @Override
+    public void println(char x) {
+        // ignored
+    }
+
+    @Override
+    public void println(int x) {
+        // ignored
+    }
+
+    @Override
+    public void println(long x) {
+        // ignored
+    }
+
+    @Override
+    public void println(float x) {
+        // ignored
+    }
+
+    @Override
+    public void println(double x) {
+        // ignored
+    }
+
+    @Override
+    public void println(char[] x) {
+        // ignored
+    }
+
+    @Override
+    public void println(String x) {
+        // ignored
+    }
+
+    @Override
+    public void println(Object x) {
+        // ignored
+    }
+
+    @Override
+    public PrintStream printf(String format, Object... args) {
+        return printf(Locale.getDefault(), format, args);
+    }
+
+    @Override
+    public PrintStream printf(Locale l, String format, Object... args) {
+        return format(l, format, args);
+    }
+
+    @Override
+    public PrintStream format(String format, Object... args) {
+        return format(Locale.getDefault(), format, args);
+    }
+
+    @Override
+    public PrintStream format(Locale l, String format, Object... args) {
+        return this;
+    }
+
+    @Override
+    public PrintStream append(CharSequence csq) {
+        return append(csq, 0, csq.length());
+    }
+
+    @Override
+    public PrintStream append(CharSequence csq, int start, int end) {
+        return this;
+    }
+
+    @Override
+    public PrintStream append(char c) {
+        return this;
+    }
+}
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
index 3f31e81..08a5230 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
@@ -37,9 +37,11 @@ public abstract class AbstractLoggingBean {
 
     /**
      * Default constructor - creates a logger using the full class name
+     *
+     * @see #AbstractLoggingBean(Logger)
      */
     protected AbstractLoggingBean() {
-        this("");
+        this((Logger) null);
     }
 
     /**
@@ -56,6 +58,14 @@ public abstract class AbstractLoggingBean {
         log = LoggerFactory.getLogger(name);
     }
 
+    /**
+     * @param logger The {@link Logger} instance to use - if {@code null} then one is retrieved using the full class
+     *               name
+     */
+    protected AbstractLoggingBean(Logger logger) {
+        log = (logger == null) ? LoggerFactory.getLogger(getClass()) : logger;
+    }
+
     protected SimplifiedLog getSimplifiedLogger() {
         SimplifiedLog logger;
         synchronized (simplifiedLog) {
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggerSkeleton.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggerSkeleton.java
new file mode 100644
index 0000000..50b320a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggerSkeleton.java
@@ -0,0 +1,169 @@
+/*
+ * 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.logging;
+
+import org.slf4j.helpers.MarkerIgnoringBase;
+
+/**
+ * Provides some more default implementations for {@link org.slf4j.Logger} interface methods
+ * 
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class LoggerSkeleton extends MarkerIgnoringBase {
+    private static final long serialVersionUID = 1129569061632973648L;
+
+    protected LoggerSkeleton(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public void error(String format, Object arg) {
+        if (isErrorEnabled()) {
+            error(format, new Object[] { arg });
+        }
+    }
+
+    @Override
+    public void error(String format, Object arg1, Object arg2) {
+        if (isErrorEnabled()) {
+            error(format, new Object[] { arg1, arg2 });
+        }
+    }
+
+    @Override
+    public void error(String format, Object... arguments) {
+        if (isErrorEnabled()) {
+            error(LoggingUtils.formatMessage(format, arguments));
+        }
+    }
+
+    @Override
+    public void error(String msg) {
+        error(msg, (Throwable) null);
+    }
+
+    @Override
+    public void warn(String format, Object arg) {
+        if (isWarnEnabled()) {
+            warn(format, new Object[] { arg });
+        }
+    }
+
+    @Override
+    public void warn(String format, Object arg1, Object arg2) {
+        if (isWarnEnabled()) {
+            warn(format, new Object[] { arg1, arg2 });
+        }
+    }
+
+    @Override
+    public void warn(String format, Object... arguments) {
+        if (isWarnEnabled()) {
+            warn(LoggingUtils.formatMessage(format, arguments));
+        }
+    }
+
+    @Override
+    public void warn(String msg) {
+        warn(msg, (Throwable) null);
+    }
+
+    @Override
+    public void info(String format, Object arg) {
+        if (isInfoEnabled()) {
+            info(format, new Object[] { arg });
+        }
+    }
+
+    @Override
+    public void info(String format, Object arg1, Object arg2) {
+        if (isInfoEnabled()) {
+            info(format, new Object[] { arg1, arg2 });
+        }
+    }
+
+    @Override
+    public void info(String format, Object... arguments) {
+        if (isInfoEnabled()) {
+            info(LoggingUtils.formatMessage(format, arguments));
+        }
+    }
+
+    @Override
+    public void info(String msg) {
+        if (isInfoEnabled()) {
+            info(msg, (Throwable) null);
+        }
+    }
+
+    @Override
+    public void debug(String format, Object arg) {
+        if (isDebugEnabled()) {
+            debug(format, new Object[] { arg });
+        }
+    }
+
+    @Override
+    public void debug(String format, Object arg1, Object arg2) {
+        if (isDebugEnabled()) {
+            debug(format, new Object[] { arg1, arg2 });
+        }
+    }
+
+    @Override
+    public void debug(String format, Object... arguments) {
+        if (isDebugEnabled()) {
+            debug(LoggingUtils.formatMessage(format, arguments));
+        }
+    }
+
+    @Override
+    public void debug(String msg) {
+        if (isDebugEnabled()) {
+            debug(msg, (Throwable) null);
+        }
+    }
+
+    @Override
+    public void trace(String format, Object arg) {
+        if (isTraceEnabled()) {
+            trace(format, new Object[] { arg });
+        }
+    }
+
+    @Override
+    public void trace(String format, Object arg1, Object arg2) {
+        if (isTraceEnabled()) {
+            trace(format, new Object[] { arg1, arg2 });
+        }
+    }
+
+    @Override
+    public void trace(String format, Object... arguments) {
+        if (isTraceEnabled()) {
+            trace(LoggingUtils.formatMessage(format, arguments));
+        }
+    }
+
+    @Override
+    public void trace(String msg) {
+        trace(msg, (Throwable) null);
+    }
+}
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
index 3d592b5..263924b 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
@@ -38,6 +38,8 @@ import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.ReflectionUtils;
 import org.slf4j.Logger;
+import org.slf4j.helpers.FormattingTuple;
+import org.slf4j.helpers.MessageFormatter;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -505,6 +507,23 @@ public final class LoggingUtils {
         };
     }
 
+    /**
+     * Formats an {@code slf4j} message using its formatting structure - mainly the usage of <U>positional</U> arguments
+     * - e.g., &quot;Value1={}, Value2={}, ...&quot;
+     *
+     * @param  format    The formatting instructions - ignored if {@code null}/empty
+     * @param  arguments The formatting arguments - ignored if {@code null}/empty
+     * @return           The formatted message - or the format itself if no arguments or no format string
+     */
+    public static String formatMessage(String format, Object... arguments) {
+        if (GenericUtils.isEmpty(format) || GenericUtils.isEmpty(arguments)) {
+            return format;
+        }
+
+        FormattingTuple tuple = MessageFormatter.arrayFormat(format, arguments, null);
+        return tuple.getMessage();
+    }
+
     public static void debug(Logger log, String message, Object o1, Object o2, Throwable t) {
         if (log.isTraceEnabled() && (t != null)) {
             log.debug(message, o1, o2, t);
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLoggerSkeleton.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLoggerSkeleton.java
new file mode 100644
index 0000000..c890696
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLoggerSkeleton.java
@@ -0,0 +1,103 @@
+/*
+ * 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.logging;
+
+import java.util.logging.Level;
+
+/**
+ * Routes the effective logging to the {@link SimplifiedLog} methods.
+ *
+ * <B>Note:</B> we need the explicit overrides even though they are defined in {@link SimplifiedLog} as {@code default}
+ * since they are defined as {@code abstract} in the {@code slf4j Logger} interface
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class SimplifiedLoggerSkeleton extends LoggerSkeleton implements SimplifiedLog {
+    public static final SimplifiedLoggerSkeleton EMPTY = new SimplifiedLoggerSkeleton("EMPTY") {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public boolean isEnabledLevel(Level level) {
+            return false;
+        }
+
+        @Override
+        public void log(Level level, Object message, Throwable t) {
+            return;
+        }
+
+    };
+
+    private static final long serialVersionUID = 9207771015837755402L;
+
+    protected SimplifiedLoggerSkeleton(String name) {
+        super(name);
+    }
+
+    @Override
+    public boolean isErrorEnabled() {
+        return SimplifiedLog.super.isErrorEnabled();
+    }
+
+    @Override
+    public void error(String msg, Throwable err) {
+        SimplifiedLog.super.error(msg, err);
+    }
+
+    @Override
+    public boolean isWarnEnabled() {
+        return SimplifiedLog.super.isWarnEnabled();
+    }
+
+    @Override
+    public void warn(String msg, Throwable err) {
+        SimplifiedLog.super.warn(msg, err);
+    }
+
+    @Override
+    public boolean isInfoEnabled() {
+        return SimplifiedLog.super.isInfoEnabled();
+    }
+
+    @Override
+    public void info(String msg, Throwable err) {
+        SimplifiedLog.super.info(msg, err);
+    }
+
+    @Override
+    public boolean isDebugEnabled() {
+        return SimplifiedLog.super.isDebugEnabled();
+    }
+
+    @Override
+    public void debug(String msg, Throwable err) {
+        SimplifiedLog.super.debug(msg, err);
+    }
+
+    @Override
+    public boolean isTraceEnabled() {
+        return SimplifiedLog.super.isTraceEnabled();
+    }
+
+    @Override
+    public void trace(String msg, Throwable err) {
+        SimplifiedLog.super.trace(msg, err);
+    }
+}
diff --git a/sshd-mina/pom.xml b/sshd-mina/pom.xml
index d2f0db8..1d8e4f2 100644
--- a/sshd-mina/pom.xml
+++ b/sshd-mina/pom.xml
@@ -126,6 +126,7 @@
                         <exclude>**/CipherTest.java</exclude>
                         <exclude>**/CompressionTest.java</exclude>
                         <exclude>**/NoServerNoClientTest.java</exclude>
+                        <exclude>**/OpenSSHCertificateTest.java</exclude>
                         <!-- exclude>**/PortForwardingTest.java</exclude -->
                         <exclude>**/MacTest.java</exclude>
                         <exclude>**/SpringConfigTest.java</exclude>


[mina-sshd] 06/15: [SSHD-1110] Replace Class#newInstance() calls with Class#getDefaultConstructor().newInstance()

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 3b1b2712c1193cbd17f4c291b9e597855e7a2075
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:02:36 2020 +0200

    [SSHD-1110] Replace Class#newInstance() calls with Class#getDefaultConstructor().newInstance()
---
 CHANGES.md                                                   |  1 +
 .../main/java/org/apache/sshd/cli/client/ScpCommandMain.java |  3 ++-
 .../java/org/apache/sshd/cli/client/SftpCommandMain.java     |  3 ++-
 .../java/org/apache/sshd/cli/client/SshClientCliSupport.java |  3 ++-
 .../java/org/apache/sshd/cli/server/SshServerCliSupport.java |  6 +++---
 .../java/org/apache/sshd/common/util/ReflectionUtils.java    | 12 ++++++++++++
 .../org/apache/sshd/common/util/threads/ThreadUtils.java     | 11 ++++++-----
 .../java/org/apache/sshd/common/cipher/ARCFOUR128Test.java   |  1 +
 .../java/org/apache/sshd/common/cipher/ARCFOUR256Test.java   |  1 +
 .../src/main/java/org/apache/sshd/agent/unix/AprLibrary.java |  2 +-
 .../sshd/common/io/BuiltinIoServiceFactoryFactories.java     |  3 ++-
 .../sshd/common/io/DefaultIoServiceFactoryFactory.java       |  9 ++++-----
 .../test/java/org/apache/sshd/DefaultSetupTestSupport.java   |  3 +++
 .../test/java/org/apache/sshd/common/cipher/CipherTest.java  |  4 +++-
 .../apache/sshd/common/signature/OpenSSHCertificateTest.java |  2 ++
 .../apache/sshd/common/signature/SignatureFactoriesTest.java |  1 +
 16 files changed, 46 insertions(+), 19 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 0da7379..229099b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -19,6 +19,7 @@
 * [SSHD-1085](https://issues.apache.org/jira/browse/SSHD-1085) Added `CliLogger` + more verbosity on `SshClientMain`
 * [SSHD-1109](https://issues.apache.org/jira/browse/SSHD-1109) Route tests JUL logging via SLF4JBridgeHandler
 * [SSHD-1109](https://issues.apache.org/jira/browse/SSHD-1109) Provide full slf4j logger capabilities to CliLogger + use it in all CLI classes
+* [SSHD-1110](https://issues.apache.org/jira/browse/SSHD-1110) Replace `Class#newInstance()` calls with `Class#getDefaultConstructor().newInstance()`
 
 ## Behavioral changes and enhancements
 
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
index 8b14d4b..662f00b 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
@@ -45,6 +45,7 @@ import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
 import org.apache.sshd.common.util.threads.ThreadUtils;
 import org.apache.sshd.scp.client.ScpClient;
@@ -196,7 +197,7 @@ public class ScpCommandMain extends SshClientCliSupport {
         try {
             ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(ScpClientCreator.class);
             Class<?> clazz = cl.loadClass(className);
-            return ScpClientCreator.class.cast(clazz.newInstance());
+            return ReflectionUtils.newInstance(clazz, ScpClientCreator.class);
         } catch (Exception e) {
             stderr.append("WARNING: Failed (").append(e.getClass().getSimpleName()).append(')')
                     .append(" to instantiate ").append(className)
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
index eb560dd..c827fde 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
@@ -60,6 +60,7 @@ import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.signature.SignatureFactory;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.io.IoUtils;
@@ -280,7 +281,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
         if (GenericUtils.isNotEmpty(factoryName)) {
             try {
                 Class<?> clazz = cl.loadClass(factoryName);
-                return SftpClientFactory.class.cast(clazz.newInstance());
+                return ReflectionUtils.newInstance(clazz, SftpClientFactory.class);
             } catch (Throwable t) {
                 System.err.append("Failed (").append(t.getClass().getSimpleName()).append(')')
                         .append(" to instantiate ").append(factoryName)
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
index 9fbd052..36df4a4 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
@@ -79,6 +79,7 @@ import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.mac.Mac;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.NoCloseOutputStream;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
@@ -579,7 +580,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(KexExtensionHandler.class);
             try {
                 Class<?> clazz = cl.loadClass(kexExtension);
-                KexExtensionHandler handler = KexExtensionHandler.class.cast(clazz.newInstance());
+                KexExtensionHandler handler = ReflectionUtils.newInstance(clazz, KexExtensionHandler.class);
                 manager.setKexExtensionHandler(handler);
             } catch (Exception e) {
                 stderr.append("ERROR: Failed (").append(e.getClass().getSimpleName()).append(')')
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
index 0c00435..d5ec09c 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
@@ -50,6 +50,7 @@ import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.keyprovider.MappedKeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.resource.PathResource;
 import org.apache.sshd.common.util.security.SecurityUtils;
@@ -182,7 +183,7 @@ public abstract class SshServerCliSupport extends CliSupport {
             for (String fqcn : classes) {
                 try {
                     Class<?> clazz = cl.loadClass(fqcn);
-                    SubsystemFactory factory = SubsystemFactory.class.cast(clazz.newInstance());
+                    SubsystemFactory factory = ReflectionUtils.newInstance(clazz, SubsystemFactory.class);
                     factory = registerSubsystemFactoryListeners(
                             server, level, stdout, stderr, options, factory);
                     subsystems.add(factory);
@@ -302,8 +303,7 @@ public abstract class SshServerCliSupport extends CliSupport {
         ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(ShellFactory.class);
         try {
             Class<?> clazz = cl.loadClass(factory);
-            Object instance = clazz.newInstance();
-            ShellFactory shellFactory = ShellFactory.class.cast(instance);
+            ShellFactory shellFactory = ReflectionUtils.newInstance(clazz, ShellFactory.class);
             return useScp ? createScpCommandFactory(level, stdout, stderr, shellFactory) : shellFactory;
         } catch (Exception e) {
             stderr.append("ERROR: Failed (").append(e.getClass().getSimpleName()).append(')')
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
index 84a84f9..ffce665 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
@@ -19,6 +19,7 @@
 
 package org.apache.sshd.common.util;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.util.Collection;
 import java.util.function.Function;
@@ -50,4 +51,15 @@ public final class ReflectionUtils {
             return false;
         }
     }
+
+    public static Object newInstance(Class<?> clazz) throws ReflectiveOperationException {
+        return newInstance(clazz, Object.class);
+    }
+
+    @SuppressWarnings("checkstyle:ThrowsCount")
+    public static <T> T newInstance(Class<?> clazz, Class<? extends T> castType) throws ReflectiveOperationException {
+        Constructor<?> ctor = clazz.getDeclaredConstructor();
+        Object instance = ctor.newInstance();
+        return castType.cast(instance);
+    }
 }
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
index 9879b74..8c20928 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
@@ -28,6 +28,8 @@ import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 
+import org.apache.sshd.common.util.ReflectionUtils;
+
 /**
  * Utility class for thread pools.
  *
@@ -75,21 +77,20 @@ public final class ThreadUtils {
     }
 
     public static <T> T createDefaultInstance(
-            Class<?> anchor, Class<T> targetType, String className)
+            Class<?> anchor, Class<? extends T> targetType, String className)
             throws ReflectiveOperationException {
         return createDefaultInstance(resolveDefaultClassLoaders(anchor), targetType, className);
     }
 
     public static <T> T createDefaultInstance(
-            ClassLoader cl, Class<T> targetType, String className)
+            ClassLoader cl, Class<? extends T> targetType, String className)
             throws ReflectiveOperationException {
         Class<?> instanceType = cl.loadClass(className);
-        Object instance = instanceType.newInstance();
-        return targetType.cast(instance);
+        return ReflectionUtils.newInstance(instanceType, targetType);
     }
 
     public static <T> T createDefaultInstance(
-            Iterable<? extends ClassLoader> cls, Class<T> targetType, String className)
+            Iterable<? extends ClassLoader> cls, Class<? extends T> targetType, String className)
             throws ReflectiveOperationException {
         for (ClassLoader cl : cls) {
             try {
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
index 1c37449..9f1fef3 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
@@ -33,6 +33,7 @@ public class ARCFOUR128Test extends BaseCipherTest {
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void testEncryptDecrypt() throws Exception {
         testEncryptDecrypt(BuiltinCiphers.arcfour128);
     }
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
index e9dddf8..682b340 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
@@ -33,6 +33,7 @@ public class ARCFOUR256Test extends BaseCipherTest {
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void testEncryptDecrypt() throws Exception {
         // for RC4 256 bits we need the JCE unlimited strength policy
         ensureCipherInformationKeySizeSupported(BuiltinCiphers.arcfour256);
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AprLibrary.java b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AprLibrary.java
index 67f0286..43921f4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AprLibrary.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AprLibrary.java
@@ -97,7 +97,7 @@ public final class AprLibrary {
     }
 
     @Override
-    @SuppressWarnings("checkstyle:NoFinalizer")
+    @SuppressWarnings({ "checkstyle:NoFinalizer", "deprecation" })
     protected void finalize() throws Throwable {
         library = null;
         Pool.destroy(pool);
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/BuiltinIoServiceFactoryFactories.java b/sshd-core/src/main/java/org/apache/sshd/common/io/BuiltinIoServiceFactoryFactories.java
index 8880f64..e64baf2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/BuiltinIoServiceFactoryFactories.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/BuiltinIoServiceFactoryFactories.java
@@ -27,6 +27,7 @@ import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.OptionalFeature;
 import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory;
+import org.apache.sshd.common.util.ReflectionUtils;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -86,7 +87,7 @@ public enum BuiltinIoServiceFactoryFactories implements NamedFactory<IoServiceFa
     public final IoServiceFactoryFactory create() {
         Class<? extends IoServiceFactoryFactory> clazz = getFactoryClass();
         try {
-            return clazz.newInstance();
+            return ReflectionUtils.newInstance(clazz, IoServiceFactoryFactory.class);
         } catch (Throwable e) {
             if (e instanceof RuntimeException) {
                 throw (RuntimeException) e;
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/DefaultIoServiceFactoryFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/io/DefaultIoServiceFactoryFactory.java
index 0062579..3695e6d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/DefaultIoServiceFactoryFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/DefaultIoServiceFactoryFactory.java
@@ -26,6 +26,7 @@ import java.util.ServiceLoader;
 import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
 import org.apache.sshd.common.util.threads.CloseableExecutorService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -143,7 +144,7 @@ public class DefaultIoServiceFactoryFactory extends AbstractIoServiceFactoryFact
         return services.removeFirst();
     }
 
-    public static <T extends IoServiceFactoryFactory> T newInstance(Class<T> clazz, String factory) {
+    public static <T extends IoServiceFactoryFactory> T newInstance(Class<? extends T> clazz, String factory) {
         BuiltinIoServiceFactoryFactories builtin = BuiltinIoServiceFactoryFactories.fromFactoryName(factory);
         if (builtin != null) {
             IoServiceFactoryFactory builtinInstance = builtin.create();
@@ -155,8 +156,7 @@ public class DefaultIoServiceFactoryFactory extends AbstractIoServiceFactoryFact
         if (cl != null) {
             try {
                 Class<?> loaded = cl.loadClass(factory);
-                Object factoryInstance = loaded.newInstance();
-                return clazz.cast(factoryInstance);
+                return ReflectionUtils.newInstance(loaded, clazz);
             } catch (Throwable t) {
                 LOGGER.trace("Exception while loading factory " + factory, t);
             }
@@ -166,8 +166,7 @@ public class DefaultIoServiceFactoryFactory extends AbstractIoServiceFactoryFact
         if (cl != clDefault) {
             try {
                 Class<?> loaded = clDefault.loadClass(factory);
-                Object factoryInstance = loaded.newInstance();
-                return clazz.cast(factoryInstance);
+                return ReflectionUtils.newInstance(loaded, clazz);
             } catch (Throwable t) {
                 LOGGER.trace("Exception while loading factory " + factory, t);
             }
diff --git a/sshd-core/src/test/java/org/apache/sshd/DefaultSetupTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/DefaultSetupTestSupport.java
index 451d620..75f3b8b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/DefaultSetupTestSupport.java
+++ b/sshd-core/src/test/java/org/apache/sshd/DefaultSetupTestSupport.java
@@ -64,6 +64,7 @@ public abstract class DefaultSetupTestSupport<M extends AbstractFactoryManager>
     }
 
     @Test   // SSHD-1004
+    @SuppressWarnings("deprecation")
     public void testNoDeprecatedCiphers() {
         assertNoDeprecatedFactoryInstanceNames(Cipher.class.getSimpleName(),
                 EnumSet.of(BuiltinCiphers.arcfour128, BuiltinCiphers.arcfour256, BuiltinCiphers.tripledescbc,
@@ -93,6 +94,7 @@ public abstract class DefaultSetupTestSupport<M extends AbstractFactoryManager>
     }
 
     @Test   // SSHD-1004
+    @SuppressWarnings("deprecation")
     public void testNoDeprecatedSignatures() {
         assertNoDeprecatedFactoryInstanceNames(Cipher.class.getSimpleName(),
                 EnumSet.of(BuiltinSignatures.dsa, BuiltinSignatures.rsa_cert, BuiltinSignatures.dsa_cert),
@@ -107,6 +109,7 @@ public abstract class DefaultSetupTestSupport<M extends AbstractFactoryManager>
     }
 
     @Test
+    @SuppressWarnings("deprecation")
     public void testNoDeprecatedMacs() {
         assertNoDeprecatedFactoryInstanceNames(
                 Mac.class.getSimpleName(), EnumSet.of(BuiltinMacs.hmacmd5, BuiltinMacs.hmacmd596, BuiltinMacs.hmacsha196),
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
index 04a3d4c..9a8eadc 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
@@ -33,6 +33,7 @@ import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.util.ReflectionUtils;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
@@ -66,6 +67,7 @@ public class CipherTest extends BaseTestSupport {
     /*
      * NOTE !!! order is important since we build from it the C2S/S2C ciphers proposal
      */
+    @SuppressWarnings("deprecation")
     private static final List<Object[]> PARAMETERS = Collections.unmodifiableList(
             Arrays.asList(
                     new Object[] { BuiltinCiphers.aes128cbc, com.jcraft.jsch.jce.AES128CBC.class, NUM_LOADTEST_ROUNDS },
@@ -199,7 +201,7 @@ public class CipherTest extends BaseTestSupport {
     static boolean checkCipher(String cipher) {
         try {
             Class<?> c = Class.forName(cipher);
-            com.jcraft.jsch.Cipher jschCipher = (com.jcraft.jsch.Cipher) (c.newInstance());
+            com.jcraft.jsch.Cipher jschCipher = ReflectionUtils.newInstance(c, com.jcraft.jsch.Cipher.class);
             jschCipher.init(com.jcraft.jsch.Cipher.ENCRYPT_MODE,
                     new byte[jschCipher.getBlockSize()],
                     new byte[jschCipher.getIVSize()]);
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSSHCertificateTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSSHCertificateTest.java
index 47fadda..dcafd1a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSSHCertificateTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSSHCertificateTest.java
@@ -98,6 +98,7 @@ public class OpenSSHCertificateTest extends BaseTestSupport {
     }
 
     @Parameters(name = "type={2}")
+    @SuppressWarnings("deprecation")
     public static List<Object[]> parameters() {
         List<Object[]> list = new ArrayList<>();
 
@@ -154,6 +155,7 @@ public class OpenSSHCertificateTest extends BaseTestSupport {
     }
 
     @Test // invalid principal, abort
+    @SuppressWarnings("deprecation")
     public void testAbortOnInvalidPrincipal() throws Exception {
         CoreModuleProperties.ABORT_ON_INVALID_CERTIFICATE.set(client, true);
         boolean thrown = false;
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
index 2809bcd..df91841 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
@@ -89,6 +89,7 @@ public class SignatureFactoriesTest extends BaseTestSupport implements KeyTypeIn
     }
 
     @Parameters(name = "type={0}, size={2}")
+    @SuppressWarnings("deprecation")
     public static List<Object[]> parameters() {
         List<Object[]> list = new ArrayList<>();
         addTests(list, KeyPairProvider.SSH_DSS, BuiltinSignatures.dsa, DSS_SIZES, DSSPublicKeyEntryDecoder.INSTANCE);


[mina-sshd] 01/15: [SSHD-1085] Added more notifications related to channel state change for detecting channel closing or closed earlier

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 0bbdc772e2b36a71af3c1bf4caf98cef1ae27819
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Wed Dec 9 21:53:16 2020 +0200

    [SSHD-1085] Added more notifications related to channel state change for detecting channel closing or closed earlier
---
 CHANGES.md                                         |   2 +
 .../org/apache/sshd/cli/client/SshClientMain.java  |   2 +-
 .../sshd/common/util/logging/LoggingUtils.java     |  40 +++---
 .../sshd/client/channel/AbstractClientChannel.java |   2 +-
 .../sshd/common/channel/AbstractChannel.java       | 144 +++++++++++++--------
 .../org/apache/sshd/common/channel/Channel.java    |  18 +++
 .../session/helpers/AbstractConnectionService.java |  24 ++--
 7 files changed, 142 insertions(+), 90 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index c51a3ca..fc90adf 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -17,3 +17,5 @@
 ## Minor code helpers
 
 ## Behavioral changes and enhancements
+
+* [SSHD-1085](https://issues.apache.org/jira/browse/SSHD-1085) Added more notifications related to channel state change for detecting channel closing or closed earlier.
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
index 7c1265e..c71f4fa 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
@@ -108,7 +108,7 @@ public class SshClientMain extends SshClientCliSupport {
                     error = true;
                     break;
                 }
-                if (GenericUtils.isEmpty(command) && target == null) {
+                if (GenericUtils.isEmpty(command) && (target == null)) {
                     target = argName;
                 } else {
                     if (command == null) {
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
index 08c90d7..19aa8e9 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
@@ -556,7 +556,7 @@ public final class LoggingUtils {
     }
 
     public static void debug(Logger log, String message, Object o1, Object o2, Throwable t) {
-        if (log.isTraceEnabled() && t != null) {
+        if (log.isTraceEnabled() && (t != null)) {
             log.debug(message, o1, o2, t);
         } else if (log.isDebugEnabled()) {
             log.debug(message, o1, o2);
@@ -564,7 +564,7 @@ public final class LoggingUtils {
     }
 
     public static void debug(Logger log, String message, Object o1, Object o2, Object o3, Throwable t) {
-        if (log.isTraceEnabled() && t != null) {
+        if (log.isTraceEnabled() && (t != null)) {
             log.debug(message, o1, o2, o3, t);
         } else if (log.isDebugEnabled()) {
             log.debug(message, o1, o2, o3);
@@ -572,7 +572,7 @@ public final class LoggingUtils {
     }
 
     public static void debug(Logger log, String message, Object o1, Object o2, Object o3, Object o4, Throwable t) {
-        if (log.isTraceEnabled() && t != null) {
+        if (log.isTraceEnabled() && (t != null)) {
             log.debug(message, o1, o2, o3, o4, t);
         } else if (log.isDebugEnabled()) {
             log.debug(message, o1, o2, o3, o4);
@@ -580,7 +580,7 @@ public final class LoggingUtils {
     }
 
     public static void debug(Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Throwable t) {
-        if (log.isTraceEnabled() && t != null) {
+        if (log.isTraceEnabled() && (t != null)) {
             log.debug(message, o1, o2, o3, o4, o5, t);
         } else if (log.isDebugEnabled()) {
             log.debug(message, o1, o2, o3, o4, o5);
@@ -590,7 +590,7 @@ public final class LoggingUtils {
     @SuppressWarnings("all")
     public static void debug(
             Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Throwable t) {
-        if (log.isTraceEnabled() && t != null) {
+        if (log.isTraceEnabled() && (t != null)) {
             log.debug(message, o1, o2, o3, o4, o5, o6, t);
         } else if (log.isDebugEnabled()) {
             log.debug(message, o1, o2, o3, o4, o5, o6);
@@ -598,7 +598,7 @@ public final class LoggingUtils {
     }
 
     public static void info(Logger log, String message, Object o1, Object o2, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.info(message, o1, o2, t);
         } else {
             log.info(message, o1, o2);
@@ -606,7 +606,7 @@ public final class LoggingUtils {
     }
 
     public static void info(Logger log, String message, Object o1, Object o2, Object o3, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.info(message, o1, o2, o3, t);
         } else {
             log.info(message, o1, o2, o3);
@@ -614,7 +614,7 @@ public final class LoggingUtils {
     }
 
     public static void warn(Logger log, String message, Object o1, Object o2, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.warn(message, o1, o2, t);
         } else {
             log.warn(message, o1, o2);
@@ -622,7 +622,7 @@ public final class LoggingUtils {
     }
 
     public static void warn(Logger log, String message, Object o1, Object o2, Object o3, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.warn(message, o1, o2, o3, t);
         } else {
             log.warn(message, o1, o2, o3);
@@ -630,7 +630,7 @@ public final class LoggingUtils {
     }
 
     public static void warn(Logger log, String message, Object o1, Object o2, Object o3, Object o4, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.warn(message, o1, o2, o3, o4, t);
         } else if (log.isDebugEnabled()) {
             log.warn(message, o1, o2, o3, o4);
@@ -638,7 +638,7 @@ public final class LoggingUtils {
     }
 
     public static void warn(Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.warn(message, o1, o2, o3, o4, o5, t);
         } else {
             log.warn(message, o1, o2, o3, o4, o5);
@@ -648,7 +648,7 @@ public final class LoggingUtils {
     @SuppressWarnings("all")
     public static void warn(
             Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.warn(message, o1, o2, o3, o4, o5, o6, t);
         } else {
             log.warn(message, o1, o2, o3, o4, o5, o6);
@@ -659,7 +659,7 @@ public final class LoggingUtils {
     public static void warn(
             Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7,
             Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.warn(message, o1, o2, o3, o4, o5, o6, o7, t);
         } else {
             log.warn(message, o1, o2, o3, o4, o5, o6, o7);
@@ -670,7 +670,7 @@ public final class LoggingUtils {
     public static void warn(
             Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object o8,
             Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.warn(message, o1, o2, o3, o4, o5, o6, o7, o8, t);
         } else {
             log.warn(message, o1, o2, o3, o4, o5, o6, o7, o8);
@@ -681,7 +681,7 @@ public final class LoggingUtils {
     public static void warn(
             Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object o8,
             Object o9, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.warn(message, o1, o2, o3, o4, o5, o6, o7, o8, o9, t);
         } else {
             log.warn(message, o1, o2, o3, o4, o5, o6, o7, o8, o9);
@@ -689,7 +689,7 @@ public final class LoggingUtils {
     }
 
     public static void error(Logger log, String message, Object o1, Object o2, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.error(message, o1, o2, t);
         } else {
             log.error(message, o1, o2);
@@ -697,7 +697,7 @@ public final class LoggingUtils {
     }
 
     public static void error(Logger log, String message, Object o1, Object o2, Object o3, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.error(message, o1, o2, o3, t);
         } else {
             log.error(message, o1, o2, o3);
@@ -705,7 +705,7 @@ public final class LoggingUtils {
     }
 
     public static void error(Logger log, String message, Object o1, Object o2, Object o3, Object o4, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.error(message, o1, o2, o3, o4, t);
         } else if (log.isDebugEnabled()) {
             log.error(message, o1, o2, o3, o4);
@@ -713,7 +713,7 @@ public final class LoggingUtils {
     }
 
     public static void error(Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.error(message, o1, o2, o3, o4, o5, t);
         } else {
             log.error(message, o1, o2, o3, o4, o5);
@@ -723,7 +723,7 @@ public final class LoggingUtils {
     @SuppressWarnings("all")
     public static void error(
             Logger log, String message, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Throwable t) {
-        if (log.isDebugEnabled() && t != null) {
+        if (log.isDebugEnabled() && (t != null)) {
             log.error(message, o1, o2, o3, o4, o5, o6, t);
         } else {
             log.error(message, o1, o2, o3, o4, o5, o6);
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
index 3857313..c82c404 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/channel/AbstractClientChannel.java
@@ -297,7 +297,7 @@ public abstract class AbstractClientChannel extends AbstractChannel implements C
         if ((openFuture != null) && openFuture.isOpened()) {
             state.add(ClientChannelEvent.OPENED);
         }
-        if (closeFuture.isClosed()) {
+        if (closeFuture.isClosed() || closeSignaled.get() || unregisterSignaled.get() || isClosed()) {
             state.add(ClientChannelEvent.CLOSED);
         }
         if (isEofSignalled()) {
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 8bb4d99..f888d74 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
@@ -69,9 +69,7 @@ import org.apache.sshd.core.CoreModuleProperties;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public abstract class AbstractChannel
-        extends AbstractInnerCloseable
-        implements Channel, ExecutorServiceCarrier {
+public abstract class AbstractChannel extends AbstractInnerCloseable implements Channel, ExecutorServiceCarrier {
 
     /**
      * Default growth factor function used to resize response buffers
@@ -89,6 +87,8 @@ public abstract class AbstractChannel
     protected final AtomicBoolean initialized = new AtomicBoolean(false);
     protected final AtomicBoolean eofReceived = new AtomicBoolean(false);
     protected final AtomicBoolean eofSent = new AtomicBoolean(false);
+    protected final AtomicBoolean unregisterSignaled = new AtomicBoolean(false);
+    protected final AtomicBoolean closeSignaled = new AtomicBoolean(false);
     protected AtomicReference<GracefulState> gracefulState = new AtomicReference<>(GracefulState.Opened);
     protected final DefaultCloseFuture gracefulFuture;
     /**
@@ -274,17 +274,17 @@ public abstract class AbstractChannel
             try {
                 result = handler.process(this, req, wantReply, buffer);
             } catch (Throwable e) {
-                debug("handleRequest({}) {} while {}#process({})[want-reply={}]: {}",
-                        this, e.getClass().getSimpleName(), handler.getClass().getSimpleName(),
-                        req, wantReply, e.getMessage(), e);
+                debug("handleRequest({}) {} while {}#process({})[want-reply={}]: {}", this,
+                        e.getClass().getSimpleName(), handler.getClass().getSimpleName(), req, wantReply,
+                        e.getMessage(), e);
                 result = RequestHandler.Result.ReplyFailure;
             }
 
             // if Unsupported then check the next handler in line
             if (RequestHandler.Result.Unsupported.equals(result)) {
                 if (traceEnabled) {
-                    log.trace("handleRequest({})[{}#process({})[want-reply={}]]: {}",
-                            this, handler.getClass().getSimpleName(), req, wantReply, result);
+                    log.trace("handleRequest({})[{}#process({})[want-reply={}]]: {}", this,
+                            handler.getClass().getSimpleName(), req, wantReply, result);
                 }
             } else {
                 sendResponse(buffer, req, result, wantReply);
@@ -305,11 +305,11 @@ public abstract class AbstractChannel
      * @throws IOException If failed to send the response (if needed)
      * @see                #handleInternalRequest(String, boolean, Buffer)
      */
-    protected void handleUnknownChannelRequest(String req, boolean wantReply, Buffer buffer)
-            throws IOException {
+    protected void handleUnknownChannelRequest(String req, boolean wantReply, Buffer buffer) throws IOException {
         RequestHandler.Result r = handleInternalRequest(req, wantReply, buffer);
         if ((r == null) || RequestHandler.Result.Unsupported.equals(r)) {
-            log.warn("handleUnknownChannelRequest({}) Unknown channel request: {}[want-reply={}]", this, req, wantReply);
+            log.warn("handleUnknownChannelRequest({}) Unknown channel request: {}[want-reply={}]", this, req,
+                    wantReply);
             sendResponse(buffer, req, RequestHandler.Result.Unsupported, wantReply);
         } else {
             sendResponse(buffer, req, r, wantReply);
@@ -335,8 +335,7 @@ public abstract class AbstractChannel
         return RequestHandler.Result.Unsupported;
     }
 
-    protected IoWriteFuture sendResponse(
-            Buffer buffer, String req, RequestHandler.Result result, boolean wantReply)
+    protected IoWriteFuture sendResponse(Buffer buffer, String req, RequestHandler.Result result, boolean wantReply)
             throws IOException {
         if (log.isDebugEnabled()) {
             log.debug("sendResponse({}) request={} result={}, want-reply={}", this, req, result, wantReply);
@@ -379,6 +378,8 @@ public abstract class AbstractChannel
                 signalChannelInitialized(l);
                 return null;
             });
+
+            notifyStateChanged("init");
         } catch (Throwable err) {
             Throwable e = GenericUtils.peelException(err);
             if (e instanceof IOException) {
@@ -387,8 +388,8 @@ public abstract class AbstractChannel
                 throw (RuntimeException) e;
             } else {
                 throw new IOException(
-                        "Failed (" + e.getClass().getSimpleName() + ") to notify channel " + this + " initialization: "
-                                      + e.getMessage(),
+                        "Failed (" + e.getClass().getSimpleName() + ") to notify channel " + this
+                                      + " initialization: " + e.getMessage(),
                         e);
             }
         }
@@ -432,6 +433,21 @@ public abstract class AbstractChannel
         return initialized.get();
     }
 
+    @Override
+    public void handleChannelRegistrationResult(
+            ConnectionService service, Session session, int channelId,
+            boolean registered) {
+        notifyStateChanged("registered=" + registered);
+        if (registered) {
+            return;
+        }
+
+        RuntimeException reason = new IllegalStateException(
+                "Channel id=" + channelId + " not registered because session is being closed: " + this);
+        signalChannelClosed(reason);
+        throw reason;
+    }
+
     protected void signalChannelOpenFailure(Throwable reason) {
         try {
             invokeChannelSignaller(l -> {
@@ -440,8 +456,9 @@ public abstract class AbstractChannel
             });
         } catch (Throwable err) {
             Throwable ignored = GenericUtils.peelException(err);
-            debug("signalChannelOpenFailure({}) failed ({}) to inform listener of open failure={}: {}",
-                    this, ignored.getClass().getSimpleName(), reason.getClass().getSimpleName(), ignored.getMessage(), ignored);
+            debug("signalChannelOpenFailure({}) failed ({}) to inform listener of open failure={}: {}", this,
+                    ignored.getClass().getSimpleName(), reason.getClass().getSimpleName(), ignored.getMessage(),
+                    ignored);
         }
     }
 
@@ -461,8 +478,8 @@ public abstract class AbstractChannel
             });
         } catch (Throwable err) {
             Throwable e = GenericUtils.peelException(err);
-            debug("notifyStateChanged({})[{}] {} while signal channel state change: {}",
-                    this, hint, e.getClass().getSimpleName(), e.getMessage(), e);
+            debug("notifyStateChanged({})[{}] {} while signal channel state change: {}", this, hint,
+                    e.getClass().getSimpleName(), e.getMessage(), e);
         } finally {
             synchronized (futureLock) {
                 futureLock.notifyAll();
@@ -528,29 +545,31 @@ public abstract class AbstractChannel
             log.debug("handleClose({}) SSH_MSG_CHANNEL_CLOSE", this);
         }
 
-        if (!isEofSent()) {
-            if (debugEnabled) {
-                log.debug("handleClose({}) prevent sending EOF", this);
+        try {
+            if (!isEofSent()) {
+                if (debugEnabled) {
+                    log.debug("handleClose({}) prevent sending EOF", this);
+                }
             }
-        }
 
-        if (gracefulState.compareAndSet(GracefulState.Opened, GracefulState.CloseReceived)) {
-            close(false);
-        } else if (gracefulState.compareAndSet(GracefulState.CloseSent, GracefulState.Closed)) {
-            gracefulFuture.setClosed();
+            if (gracefulState.compareAndSet(GracefulState.Opened, GracefulState.CloseReceived)) {
+                close(false);
+            } else if (gracefulState.compareAndSet(GracefulState.CloseSent, GracefulState.Closed)) {
+                gracefulFuture.setClosed();
+            }
+        } finally {
+            notifyStateChanged("SSH_MSG_CHANNEL_CLOSE");
         }
     }
 
     @Override
     protected Closeable getInnerCloseable() {
-        Closeable closer = builder()
-                .sequential(new GracefulChannelCloseable(), getExecutorService())
+        Closeable closer = builder().sequential(new GracefulChannelCloseable(), getExecutorService())
                 .run(toString(), () -> {
                     if (service != null) {
                         service.unregisterChannel(AbstractChannel.this);
                     }
-                })
-                .build();
+                }).build();
         closer.addCloseFutureListener(future -> clearAttributes());
         return closer;
     }
@@ -675,15 +694,33 @@ public abstract class AbstractChannel
 
         IOException err = IoUtils.closeQuietly(getLocalWindow(), getRemoteWindow());
         if (err != null) {
-            debug("Failed ({}) to pre-close window(s) of {}: {}",
-                    err.getClass().getSimpleName(), this, err.getMessage(), err);
+            debug("Failed ({}) to pre-close window(s) of {}: {}", err.getClass().getSimpleName(), this,
+                    err.getMessage(), err);
         }
 
         super.preClose();
     }
 
+    @Override
+    public void handleChannelUnregistration(ConnectionService service) {
+        if (!unregisterSignaled.getAndSet(true)) {
+            if (log.isTraceEnabled()) {
+                log.trace("handleChannelUnregistration({}) via service={}", this, service);
+            }
+        }
+
+        notifyStateChanged("unregistered");
+    }
+
     public void signalChannelClosed(Throwable reason) {
+        String event = (reason == null) ? "signalChannelClosed" : reason.getClass().getSimpleName();
         try {
+            if (!closeSignaled.getAndSet(true)) {
+                if (log.isTraceEnabled()) {
+                    log.trace("signalChannelClosed({})[{}]", this, event);
+                }
+            }
+
             invokeChannelSignaller(l -> {
                 signalChannelClosed(l, reason);
                 return null;
@@ -692,6 +729,8 @@ public abstract class AbstractChannel
             Throwable e = GenericUtils.peelException(err);
             debug("signalChannelClosed({}) {} while signal channel closed: {}", this, e.getClass().getSimpleName(),
                     e.getMessage(), e);
+        } finally {
+            notifyStateChanged(event);
         }
     }
 
@@ -708,9 +747,7 @@ public abstract class AbstractChannel
         FactoryManager manager = (session == null) ? null : session.getFactoryManager();
         ChannelListener[] listeners = {
                 (manager == null) ? null : manager.getChannelListenerProxy(),
-                (session == null) ? null : session.getChannelListenerProxy(),
-                getChannelListenerProxy()
-        };
+                (session == null) ? null : session.getChannelListenerProxy(), getChannelListenerProxy() };
 
         Throwable err = null;
         for (ChannelListener l : listeners) {
@@ -783,8 +820,9 @@ public abstract class AbstractChannel
             log.debug("handleExtendedData({}) SSH_MSG_CHANNEL_EXTENDED_DATA len={}", this, len);
         }
         if (log.isTraceEnabled()) {
-            BufferUtils.dumpHex(getSimplifiedLogger(), BufferUtils.DEFAULT_HEXDUMP_LEVEL, "handleExtendedData(" + this + ")",
-                    this, BufferUtils.DEFAULT_HEX_SEPARATOR, buffer.array(), buffer.rpos(), (int) len);
+            BufferUtils.dumpHex(getSimplifiedLogger(), BufferUtils.DEFAULT_HEXDUMP_LEVEL,
+                    "handleExtendedData(" + this + ")", this, BufferUtils.DEFAULT_HEX_SEPARATOR, buffer.array(),
+                    buffer.rpos(), (int) len);
         }
         if (isEofSignalled()) {
             // TODO consider throwing an exception
@@ -793,7 +831,9 @@ public abstract class AbstractChannel
         doWriteExtendedData(buffer.array(), buffer.rpos(), len);
     }
 
-    protected long validateIncomingDataSize(int cmd, long len /* actually a uint32 */) {
+    protected long validateIncomingDataSize(
+            int cmd,
+            long len /* actually a uint32 */) {
         if (!BufferUtils.isValidUint32Value(len)) {
             throw new IllegalArgumentException(
                     "Non UINT32 length (" + len + ") for command=" + SshConstants.getCommandMessageName(cmd));
@@ -802,23 +842,23 @@ public abstract class AbstractChannel
         /*
          * According to RFC 4254 section 5.1
          *
-         * The 'maximum packet size' specifies the maximum size of an individual data packet that can be sent to the
-         * sender
+         * The 'maximum packet size' specifies the maximum size of an individual
+         * data packet that can be sent to the sender
          *
-         * The local window reflects our preference - i.e., how much our peer should send at most
+         * The local window reflects our preference - i.e., how much our peer
+         * should send at most
          */
         Window wLocal = getLocalWindow();
         long maxLocalSize = wLocal.getPacketSize();
 
         /*
-         * The reason for the +4 is that there seems to be some confusion whether the max. packet size includes the
-         * length field or not
+         * The reason for the +4 is that there seems to be some confusion
+         * whether the max. packet size includes the length field or not
          */
         if (len > (maxLocalSize + 4L)) {
             throw new IllegalStateException(
-                    "Bad length (" + len + ") "
-                                            + " for cmd=" + SshConstants.getCommandMessageName(cmd)
-                                            + " - max. allowed=" + maxLocalSize);
+                    "Bad length (" + len + ") " + " for cmd="
+                                            + SshConstants.getCommandMessageName(cmd) + " - max. allowed=" + maxLocalSize);
         }
 
         return len;
@@ -905,7 +945,8 @@ public abstract class AbstractChannel
         Buffer buffer = s.createBuffer(SshConstants.SSH_MSG_CHANNEL_EOF, Short.SIZE);
         buffer.putInt(getRecipient());
         /*
-         * The default "writePacket" does not send packets if state is not open so we need to bypass it.
+         * The default "writePacket" does not send packets if state is not open
+         * so we need to bypass it.
          */
         return s.writePacket(buffer);
     }
@@ -946,9 +987,7 @@ public abstract class AbstractChannel
     @Override
     @SuppressWarnings("unchecked")
     public <T> T setAttribute(AttributeRepository.AttributeKey<T> key, T value) {
-        return (T) attributes.put(
-                Objects.requireNonNull(key, "No key"),
-                Objects.requireNonNull(value, "No value"));
+        return (T) attributes.put(Objects.requireNonNull(key, "No key"), Objects.requireNonNull(value, "No value"));
     }
 
     @Override
@@ -979,6 +1018,7 @@ public abstract class AbstractChannel
 
     @Override
     public String toString() {
-        return getClass().getSimpleName() + "[id=" + getId() + ", recipient=" + getRecipient() + "]" + "-" + getSession();
+        return getClass().getSimpleName() + "[id=" + getId() + ", recipient=" + getRecipient() + "]" + "-"
+               + getSession();
     }
 }
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 dae4aa1..2bfd095 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
@@ -153,6 +153,24 @@ public interface Channel
     void init(ConnectionService service, Session session, int id) throws IOException;
 
     /**
+     * Invoked after being successfully registered by the connection service - should throw a {@link RuntimeException}
+     * if not registered
+     *
+     * @param service    The {@link ConnectionService} through which the channel is registered
+     * @param session    The {@link Session} associated with the channel
+     * @param id         The locally assigned channel identifier
+     * @param registered Whether registration was successful or not
+     */
+    void handleChannelRegistrationResult(ConnectionService service, Session session, int id, boolean registered);
+
+    /**
+     * Called by the connection service to inform the channel that it has bee unregistered.
+     *
+     * @param service The {@link ConnectionService} through which the channel is unregistered
+     */
+    void handleChannelUnregistration(ConnectionService service);
+
+    /**
      * @return {@code true} if call to {@link #init(ConnectionService, Session, int)} was successfully completed
      */
     boolean isInitialized();
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
index 7a9eb07..d1f3ac0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
@@ -43,7 +43,6 @@ import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.common.Closeable;
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.channel.AbstractChannel;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.ChannelFactory;
 import org.apache.sshd.common.channel.RequestHandler;
@@ -389,7 +388,7 @@ public abstract class AbstractConnectionService
     protected Closeable getInnerCloseable() {
         return builder()
                 .sequential(forwarderHolder.get(), agentForwardHolder.get(), x11ForwardHolder.get())
-                .parallel(toString(), channels.values())
+                .parallel(toString(), getChannels())
                 .build();
     }
 
@@ -417,23 +416,12 @@ public abstract class AbstractConnectionService
             }
         }
 
-        if (!registered) {
-            handleChannelRegistrationFailure(channel, channelId);
-        }
-
         if (log.isDebugEnabled()) {
-            log.debug("registerChannel({})[id={}] {}", this, channelId, channel);
+            log.debug("registerChannel({})[id={}, registered={}] {}", this, channelId, registered, channel);
         }
-        return channelId;
-    }
 
-    protected void handleChannelRegistrationFailure(Channel channel, int channelId) throws IOException {
-        RuntimeException reason = new IllegalStateException(
-                "Channel id=" + channelId + " not registered because session is being closed: " + this);
-        AbstractChannel notifier
-                = ValidateUtils.checkInstanceOf(channel, AbstractChannel.class, "Non abstract channel for id=%d", channelId);
-        notifier.signalChannelClosed(reason);
-        throw reason;
+        channel.handleChannelRegistrationResult(this, session, channelId, registered);
+        return channelId;
     }
 
     /**
@@ -452,6 +440,10 @@ public abstract class AbstractConnectionService
         if (log.isDebugEnabled()) {
             log.debug("unregisterChannel({}) result={}", channel, result);
         }
+
+        if (result != null) {
+            result.handleChannelUnregistration(this);
+        }
     }
 
     @Override


[mina-sshd] 11/15: Upgraded Spring integration version to 5.4.2

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 2b129e0841f0f35ad18aebd46cbdc3a79eb6bfb4
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:36:21 2020 +0200

    Upgraded Spring integration version to 5.4.2
---
 sshd-spring-sftp/pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sshd-spring-sftp/pom.xml b/sshd-spring-sftp/pom.xml
index eb37c5c..fb3fb65 100644
--- a/sshd-spring-sftp/pom.xml
+++ b/sshd-spring-sftp/pom.xml
@@ -32,7 +32,7 @@
 
     <properties>
         <projectRoot>${project.basedir}/..</projectRoot>
-        <spring.integration.version>5.4.1</spring.integration.version>
+        <spring.integration.version>5.4.2</spring.integration.version>
     </properties>
 
     <dependencyManagement>


[mina-sshd] 13/15: Upgraded Checkstyle version to 8.38

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 3362c2dd09713f7b4a2370677973d08d2bedc1ae
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Thu Dec 10 13:37:56 2020 +0200

    Upgraded Checkstyle version to 8.38
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 97b132e..577567c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -879,7 +879,7 @@
                         <dependency>
                             <groupId>com.puppycrawl.tools</groupId>
                             <artifactId>checkstyle</artifactId>
-                            <version>8.37</version>
+                            <version>8.38</version>
                             <exclusions>
                                 <!-- MCHECKSTYLE-156 -->
                                 <exclusion>