From acd1ea4c142d154328b8d968ececb991b2d76f15 Mon Sep 17 00:00:00 2001 From: ypc Date: Mon, 15 Jul 2019 21:09:39 +0800 Subject: [PATCH] Adds dynamic Settings for session connection timeout Adds dynamic Settings for whether sessions are reused or not --- .../java/com/jcabi/ssh/AbstractSshShell.java | 24 +++++- src/main/java/com/jcabi/ssh/Execution.java | 83 +++++++++++++------ src/main/java/com/jcabi/ssh/Ssh.java | 25 +++++- .../java/com/jcabi/ssh/SshByPassword.java | 73 ++++++++++++++-- 4 files changed, 171 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/jcabi/ssh/AbstractSshShell.java b/src/main/java/com/jcabi/ssh/AbstractSshShell.java index 9ab650c..fad6b31 100644 --- a/src/main/java/com/jcabi/ssh/AbstractSshShell.java +++ b/src/main/java/com/jcabi/ssh/AbstractSshShell.java @@ -49,8 +49,8 @@ @EqualsAndHashCode(of = { "addr", "port", "login" }) @Getter @SuppressWarnings({ "PMD.UnusedPrivateField", "PMD.SingularField" }) -abstract class AbstractSshShell implements Shell { - +public abstract class AbstractSshShell implements Shell { + boolean disconnectAfter = true; /** * IP address of the server. */ @@ -81,6 +81,24 @@ abstract class AbstractSshShell implements Shell { this.port = prt; this.login = user; } + /** + * Constructor. + * @param adr Address that you want to connect to. + * @param prt Port that you want to reach. + * @param user User that will be used when connecting. + * @param disconnectAfter disconnect session or not + * @throws UnknownHostException when host is unknown. + */ + AbstractSshShell( + final String adr, + final int prt, + final String user, + final boolean disconnectAfter) throws UnknownHostException { + this.addr = InetAddress.getByName(adr).getHostAddress(); + this.port = prt; + this.login = user; + this.disconnectAfter = disconnectAfter; + } // @checkstyle ParameterNumberCheck (2 lines) @Override @@ -93,7 +111,7 @@ public int exec(final String command, final InputStream stdin, stdout, stderr, this.session() - ).exec(); + ).exec(this.disconnectAfter); } /** diff --git a/src/main/java/com/jcabi/ssh/Execution.java b/src/main/java/com/jcabi/ssh/Execution.java index 54618bf..defd656 100644 --- a/src/main/java/com/jcabi/ssh/Execution.java +++ b/src/main/java/com/jcabi/ssh/Execution.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2014-2017, jcabi.com * All rights reserved. - * + *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: 1) Redistributions of source code must retain the above @@ -13,7 +13,7 @@ * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. - * + *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND @@ -33,27 +33,41 @@ import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; import java.util.concurrent.TimeUnit; /** * Execution of a single command. + * * @author Georgy Vlasov (wlasowegor@gmail.com) * @version $Id$ * @since 1.4 */ interface Execution { /** - * Executes some command. + * Executes some command and disconnect + * * @return Return code of the command. * @throws IOException If fails */ int exec() throws IOException; + /** + * Executes some command. + * + * @param disconnectAfter Whether to disconnect session After executes some command + * @return Return code of the command. + * @throws IOException + */ + int exec(boolean disconnectAfter) throws IOException; + /** * Execution of a command in an SSH session. + * * @author Georgy Vlasov (wlasowegor@gmail.com) * @version $Id$ * @since 1.4 @@ -62,8 +76,7 @@ final class Default implements Execution { /** * Command. */ - private final transient String command; - + private transient String command; /** * Stdin. */ @@ -87,16 +100,17 @@ final class Default implements Execution { /** * Uses an SSH session to execute a single command and disconnect * immediately. - * @param cmd Command + * + * @param cmd Command * @param input Stdin (will be closed) - * @param out Stdout (will be closed) - * @param err Stderr (will be closed) - * @param sess SSH session (will be disconnected) + * @param out Stdout (will be closed) + * @param err Stderr (will be closed) + * @param sess SSH session (will be disconnected) * @checkstyle ParameterNumberCheck (6 lines) */ Default(final String cmd, final InputStream input, - final OutputStream out, final OutputStream err, - final Session sess) { + final OutputStream out, final OutputStream err, + final Session sess) { this.command = cmd; this.stdin = input; this.stdout = out; @@ -106,26 +120,44 @@ final class Default implements Execution { @Override public int exec() throws IOException { - try { - final ChannelExec channel = ChannelExec.class.cast( + return this.exec(true); + } + + private int execCommand() throws JSchException, IOException { + final ChannelExec channel = ChannelExec.class.cast( this.session.openChannel("exec") - ); - channel.setErrStream(this.stderr, false); - channel.setOutputStream(this.stdout, false); - channel.setInputStream(this.stdin, false); - channel.setCommand(this.command); - channel.connect(); - Logger.info(this, "$ %s", this.command); - return this.exec(channel); - } catch (final JSchException ex) { - throw new IOException(ex); - } finally { - this.session.disconnect(); + ); + channel.setErrStream(this.stderr, false); + channel.setOutputStream(this.stdout, false); + channel.setInputStream(this.stdin, false); + channel.setCommand(this.command); + channel.connect(); + Logger.info(this, "$ %s", this.command); + return this.exec(channel); + } + + @Override + public int exec(boolean disconnectAfter) throws IOException { + if (disconnectAfter) { + try { + return this.execCommand(); + } catch (final JSchException ex) { + throw new IOException(ex); + } finally { + this.session.disconnect(); + } + } else { + try { + return this.execCommand(); + } catch (final JSchException ex) { + throw new IOException(ex); + } } } /** * Exec this channel and return its exit code. + * * @param channel The channel to exec * @return Exit code (zero in case of success) * @throws IOException If fails @@ -140,6 +172,7 @@ private int exec(final ChannelExec channel) throws IOException { /** * Wait until it's done and return its code. + * * @param exec The channel * @return The exit code * @throws IOException If some IO problem inside diff --git a/src/main/java/com/jcabi/ssh/Ssh.java b/src/main/java/com/jcabi/ssh/Ssh.java index 7410d44..761d489 100644 --- a/src/main/java/com/jcabi/ssh/Ssh.java +++ b/src/main/java/com/jcabi/ssh/Ssh.java @@ -98,6 +98,10 @@ public final class Ssh extends AbstractSshShell { * Private SSH key pass phrase. */ private final transient String passphrase; + /** + * connect timeout + */ + private transient int serverAliveInterval = Tv.TEN; /** * Constructor. @@ -216,6 +220,25 @@ public Ssh(final String adr, final int prt, this.key = priv; this.passphrase = passphrs; } + /** + * Constructor. + * @param adr IP address + * @param prt Port of server + * @param user Login + * @param priv Private SSH key + * @param passphrs Pass phrase for encrypted priv. key + * @throws UnknownHostException when host is unknown. + * @checkstyle ParameterNumberCheck (6 lines) + */ + public Ssh(final String adr, final int prt, + final String user, final String priv, + final String passphrs ,final int serverAliveInterval + ) throws UnknownHostException { + super(adr, prt, user); + this.key = priv; + this.passphrase = passphrs; + this.serverAliveInterval = serverAliveInterval; + } /** * Escape SSH argument. @@ -272,7 +295,7 @@ protected Session session() throws IOException { this.getLogin(), this.getAddr(), this.getPort() ); session.setServerAliveInterval( - (int) TimeUnit.SECONDS.toMillis(Tv.TEN) + (int) TimeUnit.SECONDS.toMillis(this.serverAliveInterval) ); session.setServerAliveCountMax(Tv.MILLION); session.connect(); diff --git a/src/main/java/com/jcabi/ssh/SshByPassword.java b/src/main/java/com/jcabi/ssh/SshByPassword.java index 7df8f5a..91cd642 100644 --- a/src/main/java/com/jcabi/ssh/SshByPassword.java +++ b/src/main/java/com/jcabi/ssh/SshByPassword.java @@ -56,6 +56,12 @@ public final class SshByPassword extends AbstractSshShell { * User password. */ private final transient String password; + /** + * session timeOut. + */ + private transient int serverAliveInterval = Tv.TEN; + + private Session session = null; /** * Constructor. @@ -72,6 +78,59 @@ public SshByPassword(final String adr, final int prt, super(adr, prt, user); this.password = passwd; } + /** + * Constructor. + * @param adr IP address + * @param prt Port of server + * @param user Login + * @param passwd Password + * @param disconnectAfter disconnect session or not + * @throws UnknownHostException If fails + * @checkstyle ParameterNumberCheck (6 lines) + */ + public SshByPassword(final String adr, final int prt, + final String user, final String passwd,final boolean disconnectAfter) + throws UnknownHostException { + super(adr, prt, user); + this.password = passwd; + this.disconnectAfter = disconnectAfter; + } + /** + * Constructor. + * @param adr IP address + * @param prt Port of server + * @param user Login + * @param passwd Password + * @param serverAliveInterval (second) + * @throws UnknownHostException If fails + * @checkstyle ParameterNumberCheck (6 lines) + */ + public SshByPassword(final String adr, final int prt, + final String user, final String passwd,final int serverAliveInterval) + throws UnknownHostException { + super(adr, prt, user); + this.password = passwd; + this.serverAliveInterval = serverAliveInterval; + } + /** + * Constructor. + * @param adr IP address + * @param prt Port of server + * @param user Login + * @param passwd Password + * @param serverAliveInterval (second) + * @param disconnectAfter disconnect session or not + * @throws UnknownHostException If fails + * @checkstyle ParameterNumberCheck (6 lines) + */ + public SshByPassword(final String adr, final int prt, + final String user, final String passwd,final int serverAliveInterval,boolean disconnectAfter) + throws UnknownHostException { + super(adr, prt, user); + this.password = passwd; + this.serverAliveInterval = serverAliveInterval; + this.disconnectAfter = disconnectAfter; + } // @checkstyle ProtectedMethodInFinalClassCheck (10 lines) @Override @@ -84,24 +143,28 @@ public SshByPassword(final String adr, final int prt, types = IOException.class ) protected Session session() throws IOException { + if (this.session!=null && this.session.isConnected()){ + return this.session; + } try { JSch.setConfig("StrictHostKeyChecking", "no"); JSch.setLogger(new JschLogger()); final JSch jsch = new JSch(); Logger.debug( - this, - "Opening SSH session to %s@%s:%s (auth with password)...", - this.getLogin(), this.getAddr(), this.getPort() + this, + "Opening SSH session to %s@%s:%s (auth with password)...", + this.getLogin(), this.getAddr(), this.getPort() ); final Session session = jsch.getSession( - this.getLogin(), this.getAddr(), this.getPort() + this.getLogin(), this.getAddr(), this.getPort() ); session.setPassword(this.password); session.setServerAliveInterval( - (int) TimeUnit.SECONDS.toMillis(Tv.TEN) + (int) TimeUnit.SECONDS.toMillis(this.serverAliveInterval) ); session.setServerAliveCountMax(Tv.MILLION); session.connect(); + this.session = session; return session; } catch (final JSchException ex) { throw new IOException(ex);