package org.concordion.internal.command; import java.lang.reflect.Method; import org.concordion.api.Element; import org.concordion.api.Evaluator; import org.concordion.api.Result; import org.concordion.api.ResultRecorder; import org.concordion.api.Runner; import org.concordion.internal.CommandCall; import org.concordion.internal.RunListener; import org.concordion.internal.runner.DefaultConcordionRunner; import org.concordion.internal.util.Announcer; import org.concordion.internal.util.Check; public class RunCommand extends AbstractCommand { private Announcer<RunListener> listeners = Announcer.to(RunListener.class); public void addRunListener(RunListener runListener) { listeners.addListener(runListener); } public void removeRunListener(RunListener runListener) { listeners.removeListener(runListener); } @Override public void execute(CommandCall commandCall, Evaluator evaluator, ResultRecorder resultRecorder) { Check.isFalse(commandCall.hasChildCommands(), "Nesting commands inside an 'run' is not supported"); Element element = commandCall.getElement(); String href = element.getAttributeValue("href"); Check.notNull(href, "The 'href' attribute must be set for an element containing concordion:run"); String runnerType = commandCall.getExpression(); String expression = element.getAttributeValue("concordion:params"); if (expression != null) evaluator.evaluate(expression); String concordionRunner = null; concordionRunner = System.getProperty("concordion.runner." + runnerType); if (concordionRunner == null && "concordion".equals(runnerType)) { concordionRunner = DefaultConcordionRunner.class.getName(); } if (concordionRunner == null) { try { Class.forName(runnerType); concordionRunner = runnerType; } catch (ClassNotFoundException e1) { // OK, we're reporting this in a second. } } Check.notNull(concordionRunner, "The runner '" + runnerType + "' cannot be found. " + "Choices: (1) Use 'concordion' as your runner (2) Ensure that the 'concordion.runner." + runnerType + "' System property is set to a name of an org.concordion.Runner implementation " + "(3) Specify a full class name of an org.concordion.Runner implementation"); try { Class<?> clazz = Class.forName(concordionRunner); Runner runner = (Runner) clazz.newInstance(); for (Method method : runner.getClass().getMethods()) { String methodName = method.getName(); if (methodName.startsWith("set") && methodName.length() > 3 && method.getParameterTypes().length == 1) { String variableName = methodName.substring(3, 4).toLowerCase() + method.getName().substring(4); Object variableValue = evaluator.evaluate(variableName); if (variableValue == null) { try { variableValue = evaluator.getVariable(variableName); } catch (Exception e) { } } if (variableValue != null) { try { method.invoke(runner, variableValue); } catch (Exception e) { } } } } try { Result result = runner.execute(commandCall.getResource(), href).getResult(); if (result == Result.SUCCESS) { announceSuccess(element); } else if (result == Result.IGNORED) { announceIgnored(element); } else { announceFailure(element); } resultRecorder.record(result); } catch (Throwable e) { announceFailure(e, element, runnerType); resultRecorder.record(Result.FAILURE); } } catch (Exception e) { announceFailure(e, element, runnerType); resultRecorder.record(Result.FAILURE); } } private void announceIgnored(Element element) { listeners.announce().ignoredReported(new RunIgnoreEvent(element)); } private void announceSuccess(Element element) { listeners.announce().successReported(new RunSuccessEvent(element)); } private void announceFailure(Element element) { listeners.announce().failureReported(new RunFailureEvent(element)); } private void announceFailure(Throwable throwable, Element element, String expression) { listeners.announce().throwableCaught(new ThrowableCaughtEvent(throwable, element, expression)); } }