/* * Sakuli - Testing and Monitoring-Tool for Websites and common UIs. * * Copyright 2013 - 2015 the original author or authors. * * 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 org.sakuli.actions.environment; import org.sakuli.actions.logging.LogToResult; import org.sakuli.actions.screenbased.Region; import org.sakuli.exceptions.SakuliException; import org.sakuli.loader.BeanLoader; import org.sakuli.loader.ScreenActionLoader; import org.sikuli.basics.Settings; import org.sikuli.natives.CommandExecutorHelper; import org.sikuli.script.App; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.TimeUnit; import static org.sakuli.utils.SystemHelper.sleep; /** * @author Tobias Schneck */ public class Application extends App { private static final Logger LOGGER = LoggerFactory.getLogger(Application.class); private final boolean resumeOnException; private ScreenActionLoader loader; private Long sleepMillis = 1000L; /** * Creates a new Application of the name or path to the executable. * * @param appName name of path of the application. */ public Application(String appName) { this(appName, false); } /** * Creates a new Application of the name or path to the executable. * * @param appName name of path of the application. * @param resumeOnException if true the execution of the script wont't stop at an exception */ // TODO: add aspect for constructor @LogToResult(message = "create application", logClassInstance = false) public Application(String appName, boolean resumeOnException) { super(appName); this.loader = BeanLoader.loadScreenActionLoader(); this.resumeOnException = resumeOnException; } /** * Opens the created application. For loadtime intensiv application change the default sleep time with {@link * #setSleepTime(Integer)}. * * @return this {@link Application}. */ @LogToResult(message = "open application") @Override public Application open() { App app = super.open(); sleep(sleepMillis); if (app == null) { loader.getExceptionHandler().handleException("Application '" + getName() + " could not be opend! ... Please check the application name or path!", resumeOnException); return null; } final int tries = 5; for (int i = 0; i < tries && this.getPID() <= 0; i++) { LOGGER.info("wait {} ms more for finish loading application {} - {} of {} tries", sleepMillis, this.getName(), i, tries); sleep(sleepMillis); } LOGGER.info("\"{}\" - PID: {}", this.getName(), this.getPID()); return this; } /** * focus the current application, if the application is in the background. * * @return this {@link Application}. */ @Override public Application focus() { return focusWindow(0); } /** * focus a specific window of the application. * * @param windowNumber indemnifies the window * @return this {@link Application}. */ @LogToResult(message = "focus application in window") public Application focusWindow(Integer windowNumber) { LOGGER.debug("Focus window \"" + windowNumber + "\" in application \"" + getName() + "\"."); App app = super.focus(windowNumber); sleep(sleepMillis); if (app == null) { LOGGER.warn("Application '{}' could not be focused! ... Please check if the application has been opened before or is already focused!", getName()); return this; } return this; } /** * close the already existing application. * * @return this {@link Application}. */ public Application closeApp() { return closeApp(false); } /** * close the already existing application. * * @param silent if true, no exception will be thrown on errors and stop the test execution. * @return this {@link Application}. */ @LogToResult public Application closeApp(boolean silent) { LOGGER.info("Close application with name or path \"" + getName() + "\"."); int retValue = -1; try { retValue = super.close(); } catch (Throwable e) { LOGGER.error("ERROR in closing Application", e); } if (!silent && retValue != 0) { loader.getExceptionHandler().handleException("Application '" + getName() + " could not be closed! ... Please check if the application has been opened before!", resumeOnException); return null; } return this; } /** * Kill the already existing application hardly * * @return this {@link Application}. */ public Application kill() { return kill(false); } /** * Kill the already existing application hardly. * * @param silent if true, no exception will be thrown on errors. * @return this {@link Application}. */ @LogToResult public Application kill(boolean silent) { try { if (getPID() < 1) { return killAppName(getName()); } return killAppPID(getPID()); } catch (SakuliException e) { if (!silent) { loader.getExceptionHandler().handleException(e); return null; } LOGGER.debug(e.getMessage(), e); } return this; } private Application killAppName(String name) throws SakuliException { try { String cmd = String.format( Settings.isWindows() ? "Taskkill /IM \"%s\" /F" : "pkill \"%s\"" , name); CommandExecutorHelper.execute(cmd, 0); } catch (Exception e) { throw new SakuliException(e, String.format("could not kill application with name '%s'.", name)); } return this; } private Application killAppPID(Integer pid) throws SakuliException { try { String cmd = String.format( Settings.isWindows() ? "Taskkill /PID %d /F" : "kill -9 %d" , pid); CommandExecutorHelper.execute(cmd, 0); } catch (Exception e) { throw new SakuliException(e, String.format("could not kill application with PID '%d'.", pid)); } return this; } /** * sets the sleep time in seconds of the application actions to handle with long loading times. The default sleep * time is set to 1 seconds * * @param seconds sleep time in seconds * @return this {@link Application}. */ @LogToResult public Application setSleepTime(Integer seconds) { this.sleepMillis = TimeUnit.SECONDS.toMillis(seconds); return this; } /** * creats and returns a {@link Region} object from the application. * * @return this {@link Application} */ @LogToResult(message = "get a Region object from the application") public Region getRegion() { org.sikuli.script.Region window = super.window(); if (window == null) { loader.getExceptionHandler().handleException("Could not identify Region for application \"" + getName() + "\"", resumeOnException); return null; } return new Region(window, resumeOnException); } /** * creats and returns a {@link Region} object from the specific window of the application. * * @param windowNumber indemnifies the window * @return this {@link Application} */ @LogToResult(message = "get a Region object from the window of the application ") public Region getRegionForWindow(int windowNumber) { org.sikuli.script.Region window = super.window(windowNumber); if (window == null) { loader.getExceptionHandler().handleException("Could not identify Region for window \"" + windowNumber + "\" of application \"" + getName() + "\"", resumeOnException); return null; } return new Region(window, resumeOnException); } /** * @return the name of the current application as {@link String}. */ @Override public String getName() { return super.getName(); } @Override public String toString() { return getName(); } }