All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.seleniumtests.util.osutility.OSCommand Maven / Gradle / Ivy

There is a newer version: 5.1.13
Show newest version
/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 * 				Copyright 2017-2019 B.Hecquet
 *
 * Licensed 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 com.seleniumtests.util.osutility;

import java.io.IOException;
import java.nio.charset.Charset;
import java.time.Clock;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.logging.log4j.Logger;

import com.seleniumtests.customexception.CustomSeleniumTestsException;
import com.seleniumtests.customexception.ScenarioException;
import com.seleniumtests.util.logging.SeleniumRobotLogger;

/**
 * Common methods for Windows and Unix systems.
 */
public class OSCommand {
	
	private static final Logger logger = SeleniumRobotLogger.getLogger(OSCommand.class);
	public static final String USE_PATH = "_USE_PATH_";	
	
	private List cmdList;
	private String cmdString;
	private int timeout;
	private Charset charset;
	private ProcessBuilder pb;
	private Runtime runtime;
	
	public OSCommand(List cmdList) {
		this(cmdList, -1, null);
	}
	
	public OSCommand(List cmdList, int timeout, Charset charset) {
		this(cmdList, timeout, charset, new ProcessBuilder());
	}
	
	public OSCommand(List cmdList, int timeout, Charset charset, ProcessBuilder pb) {
		
		this.timeout = timeout;
		this.charset = charset;
		this.pb = pb;
		this.cmdList = cmdList;
	}
	
	/**
	 * Execute a process, giving the full command as a String (useful when command uses pipe, for example)
	 * @param cmdString
	 * @param timeout
	 * @param charset
	 */
	public OSCommand(String cmdString, int timeout, Charset charset) {
		this(cmdString, timeout, charset, Runtime.getRuntime());
	}
	public OSCommand(String cmdString, int timeout, Charset charset, Runtime runtime) {
		
		this.timeout = timeout;
		this.charset = charset;
		this.cmdString = cmdString;
		this.runtime = runtime;
	}
	
	/**
	 * Search a program in windows path
	 * Throw an exception if it cannot be found
	 * @param command
	 * @return
	 */
	public String searchInWindowsPath(String command) {
		String out = new OSCommand(Arrays.asList("where", command), timeout, charset, pb).execute(); 
		try {
			return out.split("\n")[out.split("\n").length - 1].trim();
			
		} catch (IndexOutOfBoundsException e) {
			throw new ScenarioException(String.format("Program %s is not in the path", command));
		}
	}

	/**
	 * Update command list
	 * @param cmd
	 * @return
	 */
	private List updateCommand(List cmd) {
		List newCmd = new ArrayList<>(cmd);
		
		if (newCmd.get(0).startsWith(USE_PATH)) {
			newCmd.set(0, newCmd.get(0).replace(USE_PATH, ""));
			
			if (OSUtility.isWindows()) {
				String path = searchInWindowsPath(newCmd.get(0));
				newCmd.set(0, path);
			}
		}
		return newCmd;
	}
	
	/**
	 * Execute the process and wait for termination
	 * @return
	 */
	public String execute() {
		Process proc = executeNoWait();
		return waitProcessTermination(proc, timeout, charset);
	}
	
	
	/**
	 * Executes the command and returns the process
	 * @return
	 */
	public Process executeNoWait() {
		if (cmdList != null) {
			cmdList = updateCommand(cmdList);
			pb.command(cmdList);
			
			try {
				return pb.start();
			} catch (IOException e) {
				throw new CustomSeleniumTestsException("cannot start process: " + cmdList.get(0), e);
			}
		} else {
			try {
				return runtime.exec(cmdString);
			} catch (IOException e) {
				throw new CustomSeleniumTestsException("cannot execute command: " + cmdString, e);
			}
		}
		
	}
	
	public static Process executeCommand(final String cmd) {
		return new OSCommand(cmd, -1, null).executeNoWait();
	}
	
	public static Process executeCommand(final String[] cmd) {
		return new OSCommand(Arrays.asList(cmd), -1, null).executeNoWait();		
	}
	
	/**
     * Execute a command in command line terminal
     * @param cmd for the end of the command execution
     * @return 
     */
    public static String executeCommandAndWait(final String[] cmd) {
    	return executeCommandAndWait(cmd, -1, null);
    }
    
    /**
     * Execute a command in command line terminal and wait at most 'timeout' seconds
     * @param cmd		An array containing the program to execute and its arguments
     * 					e.g: ["cmd.exe", "foo"]
     * 					If first item is "_USE_PATH_", then, on Windows, we will start cmd.exe first. This is because ProcessBuilder does not look for programs into the path
     * @param timeout 	number of seconds to wait for end of execution. A negative value means it will wait 30 secs
     * @return 
     */
   	public static String executeCommandAndWait(final String[] cmd, int timeout, Charset charset) {
        return new OSCommand(Arrays.asList(cmd), timeout, charset).execute();
    }
    
    /**
     * Execute a command in command line terminal
     * @param cmd for the end of the command execution
     * @return 
     */
   	public static String executeCommandAndWait(final String cmd) {
   		return executeCommandAndWait(cmd, -1, null);
   	}
   	
    /**
     * Execute a command in command line terminal and wait at most 'timeout' seconds
     * @param cmd
     * @param timeout 	number of seconds to wait for end of execution. A negative value means it will wait 30 secs
     * @param charset	charset used to read program output. If set to null, default charset will be used
     * @return 
     */
    public static String executeCommandAndWait(final String cmd, int timeout, Charset charset) {
    	return new OSCommand(cmd, timeout, charset).execute();
    }
    
    public String waitProcessTermination(Process proc, int timeout, Charset charset) {

    	try {

			return readOutput(proc, timeout, charset);
    	} catch (InterruptedException e) {
        	logger.error("Interruption: " + e.getMessage());
        	Thread.currentThread().interrupt();

		} catch (IOException e1) {
        	logger.error(e1);
        } 
    	return "";
    }
    
    private static String readOutput(Process proc, int timeout, Charset charset) throws IOException, InterruptedException {
    	
    	if (charset == null) {
    		charset = OSUtility.getCharset();
    	}

    	Clock clock = Clock.systemUTC();
		Instant end = clock.instant().plusSeconds(timeout > 0 ? timeout: 30);
    	
		StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), charset);
		StreamGobbler outputGobbler  = new StreamGobbler(proc.getInputStream(), charset);
		errorGobbler.start();
        outputGobbler.start();
        
        try {
	        boolean read = false;
	        boolean terminated = false;
	        while (end.isAfter(clock.instant()) && (!read || !terminated)) {
	        	// be sure we read all logs produced by process, event after termination
	        	if (!proc.isAlive()) {
	        		terminated = true;
	        	}
	        	
	        	read = true;
	        	
	        	Thread.sleep(100);
	        }
	        errorGobbler.halt();
	        outputGobbler.halt();
	        
	        StringBuilder error = errorGobbler.getOutput();
	        StringBuilder output = outputGobbler.getOutput();
	        
	        return output.toString() + '\n' + error.toString();
        } catch (Exception e) {
        	// in case something gets wrong, stop the threads
	        errorGobbler.halt();
	        outputGobbler.halt();
	        throw e;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy