Coverage Report - com.jcabi.ssh.Execution
 
Classes in this File Line Coverage Branch Coverage Complexity
Execution
N/A
N/A
2.8
Execution$Default
77%
24/31
100%
2/2
2.8
 
 1  
 /**
 2  
  * Copyright (c) 2014-2015, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.ssh;
 31  
 
 32  
 import com.jcabi.log.Logger;
 33  
 import com.jcraft.jsch.ChannelExec;
 34  
 import com.jcraft.jsch.JSchException;
 35  
 import com.jcraft.jsch.Session;
 36  
 import java.io.IOException;
 37  
 import java.io.InputStream;
 38  
 import java.io.OutputStream;
 39  
 import java.util.concurrent.TimeUnit;
 40  
 
 41  
 /**
 42  
  * Execution of a single command.
 43  
  * @author Georgy Vlasov (wlasowegor@gmail.com)
 44  
  * @version $Id: 717e566190e44c206674402485b5acdde48e29cc $
 45  
  * @since 1.4
 46  
  */
 47  
 interface Execution {
 48  
     /**
 49  
      * Executes some command.
 50  
      * @return Return code of the command.
 51  
      * @throws IOException If fails
 52  
      */
 53  
     int exec() throws IOException;
 54  
 
 55  
     /**
 56  
      * Execution of a command in an SSH session.
 57  
      * @author Georgy Vlasov (wlasowegor@gmail.com)
 58  
      * @version $Id: 717e566190e44c206674402485b5acdde48e29cc $
 59  
      * @since 1.4
 60  
      */
 61  
     final class Default implements Execution {
 62  
         /**
 63  
          * Command.
 64  
          */
 65  
         private final transient String command;
 66  
 
 67  
         /**
 68  
          * Stdin.
 69  
          */
 70  
         private final transient InputStream stdin;
 71  
 
 72  
         /**
 73  
          * Stdout.
 74  
          */
 75  
         private final transient OutputStream stdout;
 76  
 
 77  
         /**
 78  
          * Stderr.
 79  
          */
 80  
         private final transient OutputStream stderr;
 81  
 
 82  
         /**
 83  
          * Session.
 84  
          */
 85  
         private final transient Session session;
 86  
 
 87  
         /**
 88  
          * Uses an SSH session to execute a single command and disconnect
 89  
          * immediately.
 90  
          * @param cmd Command
 91  
          * @param input Stdin (will be closed)
 92  
          * @param out Stdout (will be closed)
 93  
          * @param err Stderr (will be closed)
 94  
          * @param sess SSH session (will be disconnected)
 95  
          * @checkstyle ParameterNumberCheck (6 lines)
 96  
          */
 97  
         Default(final String cmd, final InputStream input,
 98  
             final OutputStream out, final OutputStream err,
 99  5
             final Session sess) {
 100  5
             this.command = cmd;
 101  5
             this.stdin = input;
 102  5
             this.stdout = out;
 103  5
             this.stderr = err;
 104  5
             this.session = sess;
 105  5
         }
 106  
 
 107  
         /**
 108  
          * Execute {@link #command} in {@link #session}.
 109  
          * @return Exit code
 110  
          * @throws IOException If fails
 111  
          */
 112  
         @Override
 113  
         public int exec() throws IOException {
 114  
             try {
 115  5
                 final ChannelExec channel = ChannelExec.class.cast(
 116  
                     this.session.openChannel("exec")
 117  
                 );
 118  5
                 channel.setErrStream(this.stderr, false);
 119  5
                 channel.setOutputStream(this.stdout, false);
 120  5
                 channel.setInputStream(this.stdin, false);
 121  5
                 channel.setCommand(this.command);
 122  5
                 channel.connect();
 123  5
                 Logger.info(this, "$ %s", this.command);
 124  5
                 return this.exec(channel);
 125  0
             } catch (final JSchException ex) {
 126  0
                 throw new IOException(ex);
 127  
             } finally {
 128  5
                 this.session.disconnect();
 129  
             }
 130  
         }
 131  
 
 132  
         /**
 133  
          * Exec this channel and return its exit code.
 134  
          * @param channel The channel to exec
 135  
          * @return Exit code (zero in case of success)
 136  
          * @throws IOException If fails
 137  
          */
 138  
         private int exec(final ChannelExec channel) throws IOException {
 139  
             try {
 140  5
                 return this.code(channel);
 141  
             } finally {
 142  5
                 channel.disconnect();
 143  
             }
 144  
         }
 145  
 
 146  
         /**
 147  
          * Wait until it's done and return its code.
 148  
          * @param exec The channel
 149  
          * @return The exit code
 150  
          * @throws IOException If some IO problem inside
 151  
          */
 152  
         @SuppressWarnings("PMD.AvoidCatchingGenericException")
 153  
         private int code(final ChannelExec exec) throws IOException {
 154  8
             while (!exec.isClosed()) {
 155  
                 try {
 156  3
                     this.session.sendKeepAliveMsg();
 157  
                     // @checkstyle IllegalCatch (1 line)
 158  0
                 } catch (final Exception ex) {
 159  0
                     throw new IOException(ex);
 160  3
                 }
 161  
                 try {
 162  3
                     TimeUnit.SECONDS.sleep(1L);
 163  0
                 } catch (final InterruptedException ex) {
 164  0
                     Thread.currentThread().interrupt();
 165  0
                     throw new IOException(ex);
 166  3
                 }
 167  
             }
 168  5
             return exec.getExitStatus();
 169  
         }
 170  
     }
 171  
 }