// Copyright � 2005-2007 Canoo Engineering AG, Switzerland.
package com.canoo.webtest.extension.groovy;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.codehaus.groovy.control.CompilationFailedException;
import com.canoo.webtest.engine.StepExecutionException;
import com.canoo.webtest.engine.StepFailedException;
import com.canoo.webtest.steps.Step;
/**
* @author Unknown
* @author Marc Guillemot
*/
class GroovyInvoker
{
private static final Logger LOG = Logger.getLogger(GroovyInvoker.class);
/**
* Key used to save the binding as webtest properties and allow reuse across different Groovy steps of a webtest
*/
private static final String KEY_GROOVY_BINDING = GroovyInvoker.class.getName() + "#binding";
public void doExecute(final Step step, final String script)
{
final Binding variables = getBinding(step);
// set standard variable (need to be set even if binding is reused as these 2 variables are bound to current step)
variables.setVariable("step", step);
final DummyPrinter out = new DummyPrinter(step, Level.INFO);
variables.setVariable("out", out);
final GroovyShell shell = new GroovyShell(getClass().getClassLoader(), variables);
try
{
LOG.debug("Evaluating script: " + StringUtils.abbreviate(script, 20));
shell.evaluate(script);
}
catch (final CompilationFailedException e)
{
LOG.error("CompilationFailedException", e);
throw new StepExecutionException("Cannot compile groovy code: " + script, step, e);
}
catch (final AssertionError e) {
LOG.info("AssertionError", e);
throw new StepFailedException("Assertion failed within groovy code: " + script, step);
}
catch (final RuntimeException e)
{
LOG.error("RuntimeException", e);
throw new StepExecutionException("Error invoking groovy: " + e.getMessage(), step, e);
}
finally
{
out.flush();
}
}
/**
* Gets the binding to use for step execution. Retrieve the binding for this webtest if one exists
* or create a new one if this is the first groovy step of this webtest
* @param step the current step
* @return the binding to use for script execution
*/
Binding getBinding(final Step step)
{
Binding binding = (Binding) step.getWebtestProperties().get(KEY_GROOVY_BINDING);
if (binding == null)
{
LOG.info("No existing binding for this webtest, creating a new one");
binding = new Binding();
step.getWebtestProperties().put(KEY_GROOVY_BINDING, binding);
}
else
{
LOG.info("Reusing existing binding of this webtest.");
}
return binding;
}
}
/**
* A small bridge between groovy output and Step's logger. This doesn't need to
* be a {@link java.io.PrintWriter}: an object with methods <code>print</code>
* and <code>println</code> is enough.
*/
class DummyPrinter
{
private final Logger fLogger;
private final Level fLevel;
private final StringBuffer fBuffer = new StringBuffer();
DummyPrinter(final Step step, final Level level)
{
fLogger = Logger.getLogger(step.getClass());
fLevel = level;
}
void println(final Object object)
{
print(object);
final String message = fBuffer.toString();
fBuffer.setLength(0);
fLogger.log(fLevel, message);
}
void print(final Object object)
{
fBuffer.append(String.valueOf(object));
}
/**
* prints the remaining message (if any)
*/
void flush()
{
if (fBuffer.length() > 0)
println(""); // forces print of the current buffer
}
}