001: // Copyright © 2005-2007 Canoo Engineering AG, Switzerland.
002: package com.canoo.webtest.extension.groovy;
003:
004: import groovy.lang.Binding;
005: import groovy.lang.GroovyShell;
006:
007: import org.apache.commons.lang.StringUtils;
008: import org.apache.log4j.Level;
009: import org.apache.log4j.Logger;
010: import org.codehaus.groovy.control.CompilationFailedException;
011:
012: import com.canoo.webtest.engine.StepExecutionException;
013: import com.canoo.webtest.engine.StepFailedException;
014: import com.canoo.webtest.steps.Step;
015:
016: /**
017: * @author Unknown
018: * @author Marc Guillemot
019: */
020: class GroovyInvoker {
021: private static final Logger LOG = Logger
022: .getLogger(GroovyInvoker.class);
023: /**
024: * Key used to save the binding as webtest properties and allow reuse across different Groovy steps of a webtest
025: */
026: private static final String KEY_GROOVY_BINDING = GroovyInvoker.class
027: .getName()
028: + "#binding";
029:
030: public void doExecute(final Step step, final String script) {
031: final Binding variables = getBinding(step);
032:
033: // set standard variable (need to be set even if binding is reused as these 2 variables are bound to current step)
034: variables.setVariable("step", step);
035: final DummyPrinter out = new DummyPrinter(step, Level.INFO);
036: variables.setVariable("out", out);
037: final GroovyShell shell = new GroovyShell(getClass()
038: .getClassLoader(), variables);
039: try {
040: LOG.debug("Evaluating script: "
041: + StringUtils.abbreviate(script, 20));
042: shell.evaluate(script);
043: } catch (final CompilationFailedException e) {
044: LOG.error("CompilationFailedException", e);
045: throw new StepExecutionException(
046: "Cannot compile groovy code: " + script, step, e);
047: } catch (final AssertionError e) {
048: LOG.info("AssertionError", e);
049: throw new StepFailedException(
050: "Assertion failed within groovy code: " + script,
051: step);
052: } catch (final RuntimeException e) {
053: LOG.error("RuntimeException", e);
054: throw new StepExecutionException("Error invoking groovy: "
055: + e.getMessage(), step, e);
056: } finally {
057: out.flush();
058: }
059: }
060:
061: /**
062: * Gets the binding to use for step execution. Retrieve the binding for this webtest if one exists
063: * or create a new one if this is the first groovy step of this webtest
064: * @param step the current step
065: * @return the binding to use for script execution
066: */
067: Binding getBinding(final Step step) {
068: Binding binding = (Binding) step.getWebtestProperties().get(
069: KEY_GROOVY_BINDING);
070: if (binding == null) {
071: LOG
072: .info("No existing binding for this webtest, creating a new one");
073: binding = new Binding();
074: step.getWebtestProperties()
075: .put(KEY_GROOVY_BINDING, binding);
076: } else {
077: LOG.info("Reusing existing binding of this webtest.");
078: }
079:
080: return binding;
081: }
082:
083: }
084:
085: /**
086: * A small bridge between groovy output and Step's logger. This doesn't need to
087: * be a {@link java.io.PrintWriter}: an object with methods <code>print</code>
088: * and <code>println</code> is enough.
089: */
090: class DummyPrinter {
091: private final Logger fLogger;
092: private final Level fLevel;
093: private final StringBuffer fBuffer = new StringBuffer();
094:
095: DummyPrinter(final Step step, final Level level) {
096: fLogger = Logger.getLogger(step.getClass());
097: fLevel = level;
098: }
099:
100: void println(final Object object) {
101: print(object);
102: final String message = fBuffer.toString();
103: fBuffer.setLength(0);
104: fLogger.log(fLevel, message);
105: }
106:
107: void print(final Object object) {
108: fBuffer.append(String.valueOf(object));
109: }
110:
111: /**
112: * prints the remaining message (if any)
113: */
114: void flush() {
115: if (fBuffer.length() > 0)
116: println(""); // forces print of the current buffer
117: }
118: }
|