/*
* 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.aop;
import net.sf.sahi.config.Configuration;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.sakuli.exceptions.SakuliInitException;
import org.sakuli.loader.BeanLoader;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* Aspect for the External Sahi Libary {@link net.sf.sahi}
*
* @author tschneck Date: 17.10.13
*/
@Aspect
@Component
public class SahiCommandExecutionAspect extends BaseSakuliAspect {
/**
* Due to the fact, the parsing of the sahi method {@link net.sf.sahi.util.Utils#getCommandTokens(String)} won't
* work correctly, this {@link Around} advice use the Apache libary {@link CommandLine#parse(String)} to modify it.
* See http://community.sahipro.com/forums/discussion/8552/sahi-os-5-0-and-chrome-user-data-dir-containing-spaces-not-working.
*
* @param joinPoint the {@link ProceedingJoinPoint} of the invoked method
* @param commandString the original argument as{@link String}
* @return the result of {@link CommandLine#parse(String)}
*/
@Around("execution(* net.sf.sahi.util.Utils.getCommandTokens(..)) && args(commandString)")
public String[] getCommandTokens(ProceedingJoinPoint joinPoint, String commandString) {
Logger LOGGER = getLogger(joinPoint);
CommandLine parsed = CommandLine.parse(commandString);
String[] tokens = new String[]{parsed.getExecutable()};
tokens = ArrayUtils.addAll(tokens, parsed.getArguments());
try {
Object result = joinPoint.proceed();
if (result instanceof String[] && !Arrays.equals(tokens, (String[]) result)) {
if (commandString.startsWith("sh -c \'")) { //exclude this kind of arguments, because the won't parsed correctly
//LOGGER.info("SAHI-RESULT {}", printArray((Object[]) result));
//LOGGER.info("SAKULI-RESULT {}", printArray(tokens));
return (String[]) result;
}
LOGGER.info("MODIFIED SAHI COMMAND TOKENS: {} => {}", printArray((String[]) result), printArray(tokens));
}
} catch (Throwable e) {
LOGGER.error("Exception during execution of JoinPoint net.sf.sahi.util.Utils.getCommandTokens", e);
}
return tokens;
}
/**
* Catch exceptions that would have been droped from {@link net.sf.sahi.util.Utils} of all 'execute*' methods and
* forward it to the exception Handler.
*
* Exceptions which thrown by missing "keytool" command, will be ignored , due to the internal exception handling
* strategie at the point {@link Configuration#getKeytoolPath()}.
*
* @param joinPoint the {@link JoinPoint} of the invoked method
* @param error any {@link Throwable} thrown of the Method
*/
@AfterThrowing(pointcut = "execution(* net.sf.sahi.util.Utils.execute*(..))", throwing = "error")
public void catchSahiCommandExcecutionErrors(JoinPoint joinPoint, Throwable error) {
String argString = printArgs(joinPoint, true);
if (!argString.contains("keytool")) {
BeanLoader.loadBaseActionLoader().getExceptionHandler().handleException(new SakuliInitException(error,
"Error executing command " + argString));
}
}
/**
* Because Sahi doesn't log the unmodified command call, do the logging over this advice.
*
* @param joinPoint the {@link JoinPoint} of the invoked method
*/
@Before("execution(* net.sf.sahi.util.Utils.execute*(..))")
public void logSahiCommandExection(JoinPoint joinPoint) {
getLogger(joinPoint).debug("SAHI command execution: {}", printArgs(joinPoint, true));
}
}