Is it possible to switch wifi networks in Appium? - appium

While executing my test suite I have to switch between different wifi networks available in Android Device.
In the middle of the execution I have to change my wifi connection from Wifi_Network_1 to Wifi_Network_2 provided ssid name and password of wifi network is known. How can it be done?
I am using:
Appium server -->1.8.1
java-client --> 6.1.0
selenium-java --> 3.13.0
Since NetworkConnectionSetting has been deprciated, I am not able to find alternate for it.
I can toggle wifi on/off using
driver.toggleWifi();
But it is not suitable for my case as i need to toggle between different wifi network available using SSID name and its password.
Thanks in advance.

Hi I'am using this method due Appium deprecated their switch for wifi, and I've created my own override via console via commands from adb. It is working for me so give it a try:
public synchronized boolean wifiSetup(String udid, boolean flg) {
synchronized (this) {
String flgEnabled = (flg) ? "enable" : "disable";
List<String> output = Console.runProcess(false, "adb -s " + udid + " shell am broadcast -a io.appium.settings.wifi --es setstatus " + flgEnabled);
for (String line : output) {
System.err.println(line);
if (line.equalsIgnoreCase("Broadcast completed: result=-1"))
return true;
}
return false;
}
}
usage to switch off:
wifiSetup("xxxUDIDfromAndroid", false);
usage to switch on:
wifiSetup("xxxUDIDfromAndroid", true);
And here is part for calling console:
public class Console {
private static final String[] WIN_RUNTIME = { "cmd.exe", "/C" };
private static final String[] OS_LINUX_RUNTIME = { "/bin/bash", "-l", "-c" };
private Console() {
}
private static <T> T[] concat(T[] first, T[] second) {
T[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
#SuppressWarnings({ "hiding"})
private static <String> String[] concatStr(String[] first, String[] second) {
String[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
public static List<String> runProcess(boolean isWin, String... command) {
String[] allCommand = null;
try {
if (isWin) {
allCommand = concat(WIN_RUNTIME, command);
} else {
allCommand = concat(OS_LINUX_RUNTIME, command);
}
ProcessBuilder pb = new ProcessBuilder(allCommand);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String _temp = null;
List<String> line = new ArrayList<String>();
while ((_temp = in.readLine()) != null) {
// system.out.println("temp line: " + _temp);
line.add(_temp);
}
// system.out.println("result after command: " + line);
return line;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Hope this helps,

Related

getDeviceName and getVersion runtime using Appium

Consider I am running my tests using Appium in multiple Real device.
is it possible to get getDeviceName and getVersion runtime using Appium.
Because i need to take screenshots and save it in a folder with that Devicename
I can show you how to get a list of running devices and emulators but I have no code to get the versions they are running.
/**
* Determine already connected physical devices or already running emulators
* #author Bill Hileman
* #return String List of running/connected devices
*/
public List<String> getRunningDevicesList() {
List<String> dev = new ArrayList<String>();
//Location of the Android SDK
String sdkPath = System.getenv("ANDROID_HOME");
if (sdkPath == null) {
System.err.println("ANDROID_HOME is not set in the environment");
Assert.fail();
} else {
//Location of Android Debug Bridge
String adbPath = sdkPath + "/platform-tools/adb.exe";
if (!new File(adbPath).exists()) {
System.err.println(adbPath + " is not found");
Assert.fail();
} else {
try {
String[] commandListAVDs = new String[]{adbPath, "devices"};
System.out.println("Executing command: " + adbPath + " devices");
Process process = new ProcessBuilder(commandListAVDs).start();
BufferedReader inputStream = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = inputStream.readLine()) != null)
//ignore lines that do not contain device information
if (!(line.trim().equals("") || line.equals("List of devices attached") || line.startsWith("* daemon ")))
dev.add(line.trim());
} catch (Exception e) {
System.err.println("Unable to read device list: " + e);
e.printStackTrace();
}
System.out.println("Running Devices: " + dev.toString());
}
}
return dev;
}

Selenium grid error : Session [(null externalkey)] not available and is not among the last 1000 terminated sessions

We are executing multiple test cases in selenium grid where hub is connected to 2 machines, but every time I am running the grid, I get an error.
Error creating a webdriver. Exception message:
Session [(null externalkey)] not available and is not among the last 1000 terminated sessions.
Active sessions are[]
Command duration or timeout: 0 milliseconds
Code:
private static List<WebDriver> m_listOfWebDrivers = Collections.synchronizedList(new ArrayList<WebDriver>());
private static ThreadLocal<WebDriver> m_driverForThread = new ThreadLocal<WebDriver>() {
#Override
protected WebDriver initialValue() {
WebDriver driver = null;
try {
driver = loadDesktopDriver();
} catch (Exception e) {
e.printStackTrace();
}
Log.info("Initializing Webdriver");
m_listOfWebDrivers.add(driver);
return driver;
}
};
protected static WebDriver loadDesktopDriver() throws Exception {
WebDriver driver = null;
Log.debug("Get Driver for Browser : " + m_browser);
try {
if (!m_runOnBrowserStack && null == m_browser) {
throw new IllegalArgumentException("Browser value should be provided for test");
}
driver = getNewDriver(m_browser, "", "", m_context);
**driver.manage().timeouts().implicitlyWait(50000, TimeUnit.MILLISECONDS);** /* this was added later, still didnt work*/
} catch (Exception e) {
Log.fatal("Error creating a webdriver. Exception message : " + e.getMessage());
throw e;
}
return driver;
}
public static WebDriver getNewDriver(String browserName, String browserVersion, String platform,
ITestContext context)
throws IOException, ComboBoxElementException, TextBoxElementException, ElementException, PageException {
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.BROWSER, Level.ALL);
/**
* These capabilities will need to be re assigned according to the
* browser we are going to be launched. This is required in case of
* running on Grid only but keeping it same normal execution to avoid
* code redundancy.
*/
DesiredCapabilities desiredCapabilities = null;
if (m_runOnBrowserStack) {
desiredCapabilities = new DesiredCapabilities();
JSONObject envs = (JSONObject) m_bsConfig.get("environment");
String bsEnvironment = context.getCurrentXmlTest().getParameter("bsEnvironment");
String testName = context.getCurrentXmlTest().getName();
Log.info("Environmnet details for Test [" + testName + "] is : " + bsEnvironment);
if (null == bsEnvironment)
throw new PageException(
"Environment name does not present in XML or not passed from CLI : " + bsEnvironment);
Map<String, String> envCapabilities = (Map<String, String>) envs.get(bsEnvironment);
if (null == envCapabilities)
throw new PageException("Environment name does not present in Config file : " + bsEnvironment);
Iterator<Entry<String, String>> it = envCapabilities.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, ?> pair = (Map.Entry<String, ?>) it.next();
desiredCapabilities.setCapability(pair.getKey().toString(), pair.getValue().toString());
}
Map<String, String> commonCapabilities = (Map<String, String>) m_bsConfig.get("capabilities");
it = commonCapabilities.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, ?> pair = (Map.Entry<String, ?>) it.next();
if (desiredCapabilities.getCapability(pair.getKey().toString()) == null) {
desiredCapabilities.setCapability(pair.getKey().toString(), pair.getValue().toString());
}
}
browserName = (String) desiredCapabilities.getCapability("browser");
}
DriverSupportedBrowsers driverType = DriverSupportedBrowsers.valueOf(browserName.toUpperCase());
if (null != m_gridUrl && !m_gridUrl.isEmpty()) {
m_gridUrl += "/wd/hub";
}
switch (driverType) {
case CHROME:
String chromeDriverPath = driverHome + File.separator + FrameworkConstants.chromeDriverExeName;
if (m_runOnBrowserStack || CommonHelper.isFileExists(chromeDriverPath)) {
if (!m_runOnBrowserStack)
desiredCapabilities = DesiredCapabilities.chrome();
desiredCapabilities.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized");
options.addArguments("--disable-extensions");
// To start browser in private mode
// options.addArguments("incognito");
desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, options);
System.setProperty("webdriver.chrome.driver", chromeDriverPath);
if (null != m_gridUrl && !m_gridUrl.isEmpty()) {// for runs on
// grid
return new RemoteWebDriver(new URL(m_gridUrl), desiredCapabilities);
} else
return new RemoteWebDriver(service.getUrl(), desiredCapabilities); /* The code fails here generally with the failure*/
} else {
throw new FileNotFoundException(
"Chrome Driver path : " + chromeDriverPath + "\n\"" + FrameworkConstants.chromeDriverExeName
+ "\" not found in driver home path declared in System Environment Variable \""
+ driverHome + "\"");
}
#Parameters({ "bsEnvironment" })
#BeforeMethod(alwaysRun = true)
public void initialize(ITestContext context, Method method, #Optional String bsEnvironment)
throws IOException {
String customer = context.getCurrentXmlTest().getParameter("customer");
String testName = context.getCurrentXmlTest().getName();
setTestName(testName);
String methodName = "";
m_testMethod = method.getName();
methodName = testName + "_" + method.getName() + "_" + customer;
CommonHelper.renameRetryLog(m_logDir, methodName);
Log.setLog(m_logDir, methodName);
m_context = context;
if (m_runOnBrowserStack) {
if (null == context.getCurrentXmlTest().getParameter("bsEnvironment")
|| context.getCurrentXmlTest().getParameter("bsEnvironment").isEmpty()) {
Log.info("Adding bsEnvironment parameter for run on BrowserStack");
context.getCurrentXmlTest().addParameter("bsEnvironment", bsEnvironment);
}
}
try {
WebDriver driver = getDriverInstanceForThread();
if (null == driver)
throw new PageException("Driver is null. Initialization problem!!");
// Logging browser name and version parameters, driver and thread
// instances
String browserName = null;
String browserVersion = null;
try {
Capabilities webDriverCapablities = ((RemoteWebDriver) driver).getCapabilities();
browserName = webDriverCapablities.getBrowserName();
browserVersion = webDriverCapablities.getVersion();
} catch (ClassCastException e) {
Log.error("Unable to cast driver to RemoteWebdriver");
browserName = m_browser;
browserVersion = "NA";
}
Log.info("\n ****************** START OF TEST CASE " + method.getName() + " " + customer + ":"
+ browserName + ":" + browserVersion + "\t THREAD:" + Thread.currentThread().getId()
+ "\t WEBDRIVER:" + driver + " ****************** \n");
// driver.manage().timeouts().pageLoadTimeout(CommonConstants.PAGE_LOAD_WAIT_SEC,
// TimeUnit.SECONDS);
if (null != m_gridUrl) {
// Log the remote node ip address where the test is running
Log.info("Remote Node IP: " + CommonHelper.getIPOfRemoteNode(driver));
}
} catch (Exception e) {
e.printStackTrace();
Log.warn(e.getMessage());
}
The CMD is triggered for Node giving timeout and browser timeout:
java -Dwebdriver.chrome.driver=D:/imp/iTAF_Driver_Home/chromedriver.exe -jar selenium-server-standalone-3.3.1.jar -port 5554 -role node -hub http://10.18.15.168:5550/grid/register -timeout 86400 -browserTimeout 86000
This issue is a recurring error.

org.apache.commons.net.ftp hangs timeout

I am in trouble with org.apache.commons.net.ftp.
My client need to detect timeout when uploading (Timeout for downloads works like a charm)
if i unplugged the network cable or switch off the adsl, upload hangs and timeouts are not working. I have made several test but nothing works, it's going to make me crazy
this is my code :
public boolean uploadFile2(String localFile, String serverFile) {
try {
InputStream stO = new FileInputStream(localFile);
OutputStream stD = storeFileStream(serverFile);
setDataTimeout(dataTimeOut);
Util.copyStream(stO, stD, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, new CopyStreamAdapter() {
public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
System.out.println("On transfert " + "/" + totalBytesTransferred + "/" + bytesTransferred + "/" + streamSize);
}
});
completePendingCommand();
} catch (Exception e) {
}
return false;
}
public boolean connectAndLogin(String host, String userName, String password) throws IOException, UnknownHostException,
FTPConnectionClosedException {
boolean success = false;
setDefaultTimeout(this.defaultTimeOut);
setConnectTimeout(this.connectionTimeOut);
addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
setPassiveMode(true);
connect(host);
int reply = getReplyCode();
if (FTPReply.isPositiveCompletion(reply)) {
success = login(userName, password);
setKeepAlive(true);
setControlKeepAliveTimeout(keepAliveTimeout);
setControlKeepAliveReplyTimeout(keepAliveResponseTimeout);
setSoTimeout(soTimeOut);
setDataTimeout(dataTimeOut);
setFileTransferMode(FTP.BLOCK_TRANSFER_MODE);
setFileType(FTP.BINARY_FILE_TYPE);
}
if (!success) {
disconnect();
}
return success;
}
Any help would be appreciated, thanks
and this does not work either:
FileInputStream in = new FileInputStream(localFile);
boolean result = storeFile(serverFile, in);
in.close();
return result;

IOException Radio is off, and Out of memory, on BlackBerry

I am performing HttpConnection in my RIM Blackberry application. I am using wi-fi connection. While performing HttpConnection sometimes it is returning data, sometimes it is giving
java.io.IOException Radio is off
and
java.io.IOException Out of memory
errors. I really do not understand what is the issue exactly. I am posting here my code snippets:
public static String getRemoteData(String url) throws ConnectionNotFoundException{
StringBuffer stringBuff=new StringBuffer();
try {
HttpConnection fconImg = (HttpConnection) Connector.open(url+ NetworkUtils.getConnectionString());
InputStream input = fconImg.openInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int k = 0;
while ((k = input.read()) != -1) {
baos.write(k);
}
byte[] byteArray = baos.toByteArray();
String s = new String(byteArray);
stringBuff.append(s.trim());
return stringBuff.toString();
} catch (Exception e) {
stringBuff.append("Exception : "+e.toString());
return stringBuff.toString();
}
}
And below is my NetworkUtils Class.
import net.rim.device.api.servicebook.ServiceBook;
import net.rim.device.api.servicebook.ServiceRecord;
import net.rim.device.api.system.CoverageInfo;
import net.rim.device.api.system.DeviceInfo;
import net.rim.device.api.system.RadioInfo;
import net.rim.device.api.system.WLANInfo;
public class NetworkUtils {
private static final String COVERAGE_CARRIER = "Carrier full Coverage";
private static final String COVERAGE_MDS = "BES coverage";
private static final String COVERAGE_NONE = "No coverage";
private static final String NOT_SUPPORTED_WAF = "Not supported by the device";
public static String logM;
/**
* Access the net.rim.device.api.system.DeviceInfo class in order to
* understand if the running system is a simulator or a real device.
* #return true if the current application is running on a Blackberry
* simulator, false otherwise
*/
public static boolean isSimulator() {
return DeviceInfo.isSimulator();
}
/**
* Give the information about the presence o a wifi bearer on the device
* #return true if the wifi communication interface bearer is supported by
* the device, false otherwise
*/
protected static boolean isWifiAvailable() {
// Log.info("Checking WIFI Availability");
boolean isWifiEnabled;
if (RadioInfo.areWAFsSupported(RadioInfo.WAF_WLAN)) {
// Log.info("WIFI Supported");
isWifiEnabled = true;
} else {
// Log.info("WIFI NOT Supported");
isWifiEnabled = false;
}
return isWifiEnabled;
}
/**
* Give information about the presence of active wifi connections.
* #return true if the device is connected to a wifi network with its wifi
* bearer, false otherwise
*/
protected static boolean isWifiActive() {
int active = RadioInfo.getActiveWAFs();
int wifi = RadioInfo.WAF_WLAN;
return active >= wifi;
}
protected static boolean isWapGprsDataBearerOffline() {
return RadioInfo.getState()==RadioInfo.STATE_OFF ||
RadioInfo.getSignalLevel() == RadioInfo.LEVEL_NO_COVERAGE;
}
public static String getNetworkCoverageReport() {
StringBuffer sb = new StringBuffer();
sb.append("\n*********************************************************");
sb.append("\nWireless Access Families:");
sb.append("\n3GPP: " + getNetworkCoverage(RadioInfo.WAF_3GPP));
sb.append("\nCDMA: " + getNetworkCoverage(RadioInfo.WAF_CDMA));
sb.append("\nWLAN: " + getNetworkCoverage(RadioInfo.WAF_WLAN));
sb.append("\nCDMA: " + getNetworkCoverage(RadioInfo.NETWORK_CDMA));
sb.append("\nBands:");
sb.append("\nCDMA_800: " + getNetworkCoverage(RadioInfo.BAND_CDMA_800));
sb.append("\nCDMA_1900: " + getNetworkCoverage(RadioInfo.BAND_CDMA_1900));
sb.append("\nNetworks:");
sb.append("\n802_11: " + getNetworkCoverage(RadioInfo.NETWORK_802_11));
sb.append("\nGPRS: " + getNetworkCoverage(RadioInfo.NETWORK_GPRS));
sb.append("\nNetwork services:");
sb.append("\nVOICE: " + getNetworkCoverage(RadioInfo.NETWORK_SERVICE_VOICE));
sb.append("\nUMTS: " + getNetworkCoverage(RadioInfo.NETWORK_SERVICE_UMTS));
sb.append("\nEDGE: " + getNetworkCoverage(RadioInfo.NETWORK_SERVICE_EDGE));
sb.append("\n*********************************************************");
return sb.toString();
}
private static String getNetworkCoverage(int networkType) {
if (RadioInfo.areWAFsSupported(networkType)) {
int status = CoverageInfo.getCoverageStatus(networkType, false);
switch (status) {
// case CoverageInfo.COVERAGE_DIRECT: //TODO if we switch back to < 4.5 we must use CARRIER
// return COVERAGE_CARRIER;//not support less ver of 4.5
case CoverageInfo.COVERAGE_MDS:
return COVERAGE_MDS;
case CoverageInfo.COVERAGE_NONE:
return COVERAGE_NONE;
default:
break;
}
}
return NOT_SUPPORTED_WAF;
}
public static boolean isDataConnectionAvailable() {
boolean ret = (isWifiAvailable()&&isWifiActive())||!isWapGprsDataBearerOffline();
return ret;
}
/**
* Determines what connection type to use and returns the necessary string to use it.
* #return A string with the connection info
*/
public static String getSubURL()
{
// This code is based on the connection code developed by Mike Nelson of AccelGolf.
// http://blog.accelgolf.com/2009/05/22/blackberry-cross-carrier-and-cross-network-http-connection
String connectionString = null;
// Simulator behavior is controlled by the USE_MDS_IN_SIMULATOR variable.
if(DeviceInfo.isSimulator())
{
logMessage("Device is a simulator and USE_MDS_IN_SIMULATOR is false");
connectionString = ";deviceside=true";
}
// Wifi is the preferred transmission method
else if(WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED)
{
logMessage("Device is connected via Wifi.");
connectionString = ";interface=wifi";
}else{
String uid = null;
ServiceBook sb = ServiceBook.getSB();
ServiceRecord[] records = sb.findRecordsByCid("WPTCP");
for (int i = 0; i < records.length; i++) {
if (records[i].isValid() && !records[i].isDisabled()) {
if (records[i].getUid() != null &&
records[i].getUid().length() != 0) {
if ((records[i].getCid().toLowerCase().indexOf("wptcp") != -1) &&
(records[i].getUid().toLowerCase().indexOf("wifi") == -1) &&
(records[i].getUid().toLowerCase().indexOf("mms") == -1) ) {
uid = records[i].getUid();
break;
}
}
}
}
if (uid != null) {
// WAP2 Connection
connectionString = ";ConnectionUID="+uid;
} else {
connectionString = ";deviceside=true";
}
}
return connectionString + ";ConnectionTimeout=60000";
}
/**
* Looks through the phone's service book for a carrier provided BIBS network
* #return The uid used to connect to that network.
*/
private static String getCarrierBIBSUid()
{
ServiceRecord[] records = ServiceBook.getSB().getRecords();
int currentRecord;
for(currentRecord = 0; currentRecord < records.length; currentRecord++)
{
if(records[currentRecord].getCid().toLowerCase().equals("ippp"))
{
if(records[currentRecord].getName().toLowerCase().indexOf("bibs") >= 0)
{
return records[currentRecord].getUid();
}
}
}
return null;
}
public static void logMessage(String str)
{
logM=str;
}
public synchronized static String getConnectionString() {
String connectionString = null;
// Simulator behaviour is controlled by the USE_MDS_IN_SIMULATOR variable.
if (DeviceInfo.isSimulator()) {
connectionString = ";deviceside=true";
}
// Wifi is the preferred transmission method
else if (WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED) {
connectionString = ";interface=wifi";
}
// Is the carrier network the only way to connect?
else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_DIRECT) == CoverageInfo.COVERAGE_DIRECT) {
String carrierUid = getCarrierBIBSUid();
if (carrierUid == null) {
// Has carrier coverage, but not BIBS. So use the carrier's TCP network
connectionString = ";deviceside=true";
} else {
// otherwise, use the Uid to construct a valid carrier BIBS request
connectionString = ";deviceside=false;connectionUID="+carrierUid + ";ConnectionType=mds-public";
}
}
// Check for an MDS connection instead (BlackBerry Enterprise Server)
else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_MDS) == CoverageInfo.COVERAGE_MDS) {
connectionString = ";deviceside=false";
}
// If there is no connection available abort to avoid hassling the user
// unnecssarily.
else if (CoverageInfo.getCoverageStatus() == CoverageInfo.COVERAGE_NONE) {
connectionString = "none";
}
// In theory, all bases are covered by now so this shouldn't be reachable.But hey, just in case ...
else {
connectionString = ";deviceside=true";
}
return connectionString;
}
}
Thanks very much in advance....
I don't know if it's the problem but you are not closing your ByteArrayOutputStream (call baos.close() ) before calling .toByteArray()

sshexec ant task: environment variables

I'm using SSHExec ant task to connect to a remote host and I depend on the environment variables that are set on the remote host in order to be able to successfully execute some commands.
<sshexec host="somehost"
username="${username}"
password="${password}"
command="set"/>
Using the task the env. variables that are outputed are not the same as the ones I get when I log in using an SSH Client.
How can I make the env. variables of the remote host avaiable for the session?
Actually there is something you can do about the fact it doesn't start a shell. Use the following:
<sshexec command="/bin/bash -l yourScript.sh" .../>
Using /bin/bash -l will start an login shell then execute your script within that shell. It would be exactly as if you had a version of sshexec that properly starts up a login shell. It has to be a script. If you want to run a single executable command you can do this:
<sshexec command="/bin/bash -l -c 'echo $CATALINA_HOME'" .../>
I've found out that the current SSHExeec task implementation is using JSCh's ChannelExec (remote execution of commands) instead of a ChannelShell (remote shell) as connection channel.
That means that apparentely as per JSCh's current implementation a ChannelExec doesn't load env. variables.
I'm still not sure wether this is a limitation on the protocol or on the API.
The conclusion is that as for now there's no solution for the problem, unless you implement your own Ant task.
A working draft of how it would be:
package org.apache.tools.ant.taskdefs.optional.ssh;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.StringReader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.KeepAliveOutputStream;
import org.apache.tools.ant.util.TeeOutputStream;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* Executes a command on a remote machine via ssh.
* #since Ant 1.6 (created February 2, 2003)
*/
public class SSHExecShellSupport extends SSHBase {
private static final String COMMAND_SEPARATOR = System.getProperty("line.separator");
private static final int BUFFER_SIZE = 8192;
private static final int RETRY_INTERVAL = 500;
/** the command to execute via ssh */
private String command = null;
/** units are milliseconds, default is 0=infinite */
private long maxwait = 0;
/** for waiting for the command to finish */
private Thread thread = null;
private String outputProperty = null; // like <exec>
private File outputFile = null; // like <exec>
private boolean append = false; // like <exec>
private Resource commandResource = null;
private boolean isShellMode;
private long maxTimeWithoutAnyData = 1000*10;
private static final String TIMEOUT_MESSAGE =
"Timeout period exceeded, connection dropped.";
public long getMaxTimeWithoutAnyData() {
return maxTimeWithoutAnyData;
}
public void setMaxTimeWithoutAnyData(long maxTimeWithoutAnyData) {
this.maxTimeWithoutAnyData = maxTimeWithoutAnyData;
}
public boolean isShellMode() {
return isShellMode;
}
public void setShellMode(boolean isShellMode) {
this.isShellMode = isShellMode;
}
/**
* Constructor for SSHExecTask.
*/
public SSHExecShellSupport() {
super();
}
/**
* Sets the command to execute on the remote host.
*
* #param command The new command value
*/
public void setCommand(String command) {
this.command = command;
}
/**
* Sets a commandResource from a file
* #param f the value to use.
* #since Ant 1.7.1
*/
public void setCommandResource(String f) {
this.commandResource = new FileResource(new File(f));
}
/**
* The connection can be dropped after a specified number of
* milliseconds. This is sometimes useful when a connection may be
* flaky. Default is 0, which means "wait forever".
*
* #param timeout The new timeout value in seconds
*/
public void setTimeout(long timeout) {
maxwait = timeout;
}
/**
* If used, stores the output of the command to the given file.
*
* #param output The file to write to.
*/
public void setOutput(File output) {
outputFile = output;
}
/**
* Determines if the output is appended to the file given in
* <code>setOutput</code>. Default is false, that is, overwrite
* the file.
*
* #param append True to append to an existing file, false to overwrite.
*/
public void setAppend(boolean append) {
this.append = append;
}
/**
* If set, the output of the command will be stored in the given property.
*
* #param property The name of the property in which the command output
* will be stored.
*/
public void setOutputproperty(String property) {
outputProperty = property;
}
/**
* Execute the command on the remote host.
*
* #exception BuildException Most likely a network error or bad parameter.
*/
public void execute() throws BuildException {
if (getHost() == null) {
throw new BuildException("Host is required.");
}
if (getUserInfo().getName() == null) {
throw new BuildException("Username is required.");
}
if (getUserInfo().getKeyfile() == null
&& getUserInfo().getPassword() == null) {
throw new BuildException("Password or Keyfile is required.");
}
if (command == null && commandResource == null) {
throw new BuildException("Command or commandResource is required.");
}
if(isShellMode){
shellMode();
} else {
commandMode();
}
}
private void shellMode() {
final Object lock = new Object();
Session session = null;
try {
session = openSession();
final Channel channel=session.openChannel("shell");
final PipedOutputStream pipedOS = new PipedOutputStream();
PipedInputStream pipedIS = new PipedInputStream(pipedOS);
final Thread commandProducerThread = new Thread("CommandsProducerThread"){
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(commandResource.getInputStream()));
String singleCmd;
synchronized (lock) {
lock.wait(); // waits for the reception of the very first data (before commands are issued)
while ((singleCmd = br.readLine()) != null) {
singleCmd += COMMAND_SEPARATOR;
log("cmd : " + singleCmd, Project.MSG_INFO);
pipedOS.write(singleCmd.getBytes());
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
log(e, Project.MSG_VERBOSE);
break;
}
}
log("Finished producing commands", Project.MSG_VERBOSE);
}
} catch (IOException e) {
log(e, Project.MSG_VERBOSE);
} catch (InterruptedException e) {
log(e, Project.MSG_VERBOSE);
} finally {
FileUtils.close(br);
}
}
};
ByteArrayOutputStream out = new ByteArrayOutputStream();
final TeeOutputStream tee = new TeeOutputStream(out, new KeepAliveOutputStream(System.out));
channel.setOutputStream(tee);
channel.setExtOutputStream(tee);
channel.setInputStream(pipedIS);
channel.connect();
// waits for it to finish receiving data response and then ask for another the producer to issue one more command
thread = new Thread("DataReceiverThread") {
public void run() {
long lastTimeConsumedData = System.currentTimeMillis(); // initializes the watch
try {
InputStream in = channel.getInputStream();
byte[] tmp = new byte[1024];
while (true) {
if(thread == null){ // works with maxTimeout (for the whole task to complete)
break;
}
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
lastTimeConsumedData = System.currentTimeMillis();
if (i < 0){
break;
}
tee.write(tmp, 0, i);
}
if (channel.isClosed()) {
log("exit-status: " + channel.getExitStatus(), Project.MSG_INFO);
log("channel.isEOF(): " + channel.isEOF(), Project.MSG_VERBOSE);
log("channel.isConnected(): " + channel.isConnected(), Project.MSG_VERBOSE);
throw new BuildException("Connection lost."); // NOTE: it also can happen that if one of the command are "exit" the channel will be closed!
}
synchronized(lock){
long elapsedTimeWithoutData = (System.currentTimeMillis() - lastTimeConsumedData);
if (elapsedTimeWithoutData > maxTimeWithoutAnyData) {
log(elapsedTimeWithoutData / 1000 + " secs elapsed without any data reception. Notifying command producer.", Project.MSG_VERBOSE);
lock.notify(); // command producer is waiting for this
try {
lock.wait(500); // wait til we have new commands.
Thread.yield();
log("Continuing consumer loop. commandProducerThread.isAlive()?" + commandProducerThread.isAlive(), Project.MSG_VERBOSE);
if(!commandProducerThread.isAlive()){
log("No more commands to be issued and it's been too long without data reception. Exiting consumer.", Project.MSG_VERBOSE);
break;
}
} catch (InterruptedException e) {
log(e, Project.MSG_VERBOSE);
break;
}
lastTimeConsumedData = System.currentTimeMillis(); // resets watch
}
}
}
} catch (IOException e) {
throw new BuildException(e);
}
}
};
thread.start();
commandProducerThread.start();
thread.join(maxwait);
if (thread.isAlive()) {
// ran out of time
thread = null;
if (getFailonerror()) {
throw new BuildException(TIMEOUT_MESSAGE);
} else {
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
}
} else {
//success
if (outputFile != null) {
writeToFile(out.toString(), append, outputFile);
}
// this is the wrong test if the remote OS is OpenVMS,
// but there doesn't seem to be a way to detect it.
log("Exit status (not reliable): " + channel.getExitStatus(), Project.MSG_INFO);
// int ec = channel.getExitStatus(); FIXME
// if (ec != 0) {
// String msg = "Remote command failed with exit status " + ec;
// if (getFailonerror()) {
// throw new BuildException(msg);
// } else {
// log(msg, Project.MSG_ERR);
// }
// }
}
} catch (Exception e){
throw new BuildException(e);
} finally {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
private void commandMode() {
Session session = null;
try {
session = openSession();
/* called once */
if (command != null) {
log("cmd : " + command, Project.MSG_INFO);
ByteArrayOutputStream out = executeCommand(session, command);
if (outputProperty != null) {
//#bugzilla 43437
getProject().setNewProperty(outputProperty, command + " : " + out);
}
} else { // read command resource and execute for each command
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(commandResource.getInputStream()));
String cmd;
String output = "";
while ((cmd = br.readLine()) != null) {
log("cmd : " + cmd, Project.MSG_INFO);
ByteArrayOutputStream out = executeCommand(session, cmd);
output += cmd + " : " + out + "\n";
}
if (outputProperty != null) {
//#bugzilla 43437
getProject().setNewProperty(outputProperty, output);
}
FileUtils.close(br);
} catch (IOException e) {
throw new BuildException(e);
}
}
} catch (JSchException e) {
throw new BuildException(e);
} finally {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
private ByteArrayOutputStream executeCommand(Session session, String cmd)
throws BuildException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
TeeOutputStream tee = new TeeOutputStream(out, new KeepAliveOutputStream(System.out));
try {
final ChannelExec channel;
session.setTimeout((int) maxwait);
/* execute the command */
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(cmd);
channel.setOutputStream(tee);
channel.setExtOutputStream(tee);
channel.connect();
// wait for it to finish
thread =
new Thread() {
public void run() {
while (!channel.isClosed()) {
if (thread == null) {
return;
}
try {
sleep(RETRY_INTERVAL);
} catch (Exception e) {
// ignored
}
}
}
};
thread.start();
thread.join(maxwait);
if (thread.isAlive()) {
// ran out of time
thread = null;
if (getFailonerror()) {
throw new BuildException(TIMEOUT_MESSAGE);
} else {
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
}
} else {
//success
if (outputFile != null) {
writeToFile(out.toString(), append, outputFile);
}
// this is the wrong test if the remote OS is OpenVMS,
// but there doesn't seem to be a way to detect it.
int ec = channel.getExitStatus();
if (ec != 0) {
String msg = "Remote command failed with exit status " + ec;
if (getFailonerror()) {
throw new BuildException(msg);
} else {
log(msg, Project.MSG_ERR);
}
}
}
} catch (BuildException e) {
throw e;
} catch (JSchException e) {
if (e.getMessage().indexOf("session is down") >= 0) {
if (getFailonerror()) {
throw new BuildException(TIMEOUT_MESSAGE, e);
} else {
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
}
} else {
if (getFailonerror()) {
throw new BuildException(e);
} else {
log("Caught exception: " + e.getMessage(),
Project.MSG_ERR);
}
}
} catch (Exception e) {
if (getFailonerror()) {
throw new BuildException(e);
} else {
log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
}
}
return out;
}
/**
* Writes a string to a file. If destination file exists, it may be
* overwritten depending on the "append" value.
*
* #param from string to write
* #param to file to write to
* #param append if true, append to existing file, else overwrite
* #exception Exception most likely an IOException
*/
private void writeToFile(String from, boolean append, File to)
throws IOException {
FileWriter out = null;
try {
out = new FileWriter(to.getAbsolutePath(), append);
StringReader in = new StringReader(from);
char[] buffer = new char[BUFFER_SIZE];
int bytesRead;
while (true) {
bytesRead = in.read(buffer);
if (bytesRead == -1) {
break;
}
out.write(buffer, 0, bytesRead);
}
out.flush();
} finally {
if (out != null) {
out.close();
}
}
}
}
Another simple workaround is to source the user's .bash_profile before running your commands:
<sshexec host="somehost"
username="${username}"
password="${password}"
command="source ~/.bash_profile && set"/>
Great post chubbsondubs. I needed to set the ORACLE SID then execute a PLSQL script that does not have the proper exit. Hence the echo exit piped.
<sshexec host="${db.ipaddr}"
verbose="true"
trust="true"
username="${scp.oracle.userid}"
password="${scp.oracle.password}"
command="echo exit | /bin/bash -l -c 'export ORACLE_SID=${db.name} ; sqlplus ${db.dbo.userid}/${db.dbo.password} #./INSTALL_REVPORT/CreateDatabase/gengrant.sql'"
/>

Resources