Пример #1
0
/** Created by: Steve Neal Date: 29/09/11 */
@SuppressWarnings("unchecked")
public class ChorusInterpreter {

  private ChorusLog log = ChorusLogFactory.getLog(ChorusInterpreter.class);

  private static final ScheduledExecutorService timeoutExcecutor =
      NamedExecutors.newSingleThreadScheduledExecutor("TimeoutExecutor");

  private long scenarioTimeoutMillis = 360000;
  private List<String> basePackages = Collections.emptyList();

  private ExecutionListenerSupport executionListenerSupport;

  private HandlerClassDiscovery handlerClassDiscovery = new HandlerClassDiscovery();
  private SpringContextSupport springContextSupport = new SpringContextSupport();

  private ScheduledFuture scenarioTimeoutInterrupt;
  private ScheduledFuture scenarioTimeoutStopThread;
  private ScheduledFuture scenarioTimeoutKill;

  private StepProcessor stepProcessor;

  private SubsystemManager subsystemManager;
  private HashMap<String, Class> allHandlerClasses;

  public ChorusInterpreter(ExecutionListenerSupport executionListenerSupport) {
    this.executionListenerSupport = executionListenerSupport;
    stepProcessor = new StepProcessor(executionListenerSupport);
  }

  public void initialize() {
    // load all available handler classes
    allHandlerClasses = handlerClassDiscovery.discoverHandlerClasses(basePackages);
  }

  public void runFeatures(ExecutionToken executionToken, List<FeatureToken> features) {
    // RUN EACH FEATURE
    for (FeatureToken feature : features) {
      try {
        runFeature(executionToken, feature);
      } catch (Throwable t) {
        log.error("Exception while running feature " + feature, t);
        executionToken.incrementFeaturesFailed();
      }
    }
  }

  private void runFeature(ExecutionToken executionToken, FeatureToken feature) {

    executionListenerSupport.notifyFeatureStarted(executionToken, feature);
    // notify we started, even if there are missing handlers
    // (but nothing will be done)
    // this is still important so execution listeners at least see the feature (but will show as
    // 'unimplemented')
    String config = feature.isConfiguration() ? " in config " + feature.getConfigurationName() : "";
    log.info("Running feature from file: " + feature.getFeatureFile() + config);

    // check that the required handler classes are all available and list them in order of
    // precedence
    List<Class> orderedHandlerClasses = new ArrayList<>();
    StringBuilder unavailableHandlersMessage =
        handlerClassDiscovery.findHandlerClasses(allHandlerClasses, feature, orderedHandlerClasses);
    boolean foundAllHandlerClasses = unavailableHandlersMessage.length() == 0;

    // run the scenarios in the feature
    if (foundAllHandlerClasses) {
      log.debug("The following handlers will be used " + orderedHandlerClasses);
      List<ScenarioToken> scenarios = feature.getScenarios();

      try {
        HandlerManager handlerManager =
            new HandlerManager(
                feature,
                orderedHandlerClasses,
                springContextSupport,
                subsystemManager,
                executionToken.getProfile());
        handlerManager.createFeatureScopedHandlers();
        handlerManager.processStartOfFeature();

        runScenarios(executionToken, feature, scenarios, handlerManager);

        handlerManager.processEndOfFeature();
      } catch (Exception e) {
        log.error("Exception while running feature " + feature.getName(), e);
      }

      String description =
          feature.getEndState() == EndState.PASSED
              ? " passed! "
              : feature.getEndState() == EndState.PENDING ? " pending! " : " failed! ";
      log.trace("The feature " + description);

      if (feature.getEndState() == EndState.PASSED) {
        executionToken.incrementFeaturesPassed();
      } else if (feature.getEndState() == EndState.PENDING) {
        executionToken.incrementFeaturesPending();
      } else {
        executionToken.incrementFeaturesFailed();
      }
    } else {
      log.warn(
          "The following handlers were not available, failing feature "
              + feature.getName()
              + " "
              + unavailableHandlersMessage);
      feature.setUnavailableHandlersMessage(unavailableHandlersMessage.toString());
      executionToken.incrementUnavailableHandlers();
      executionToken.incrementFeaturesFailed();
    }

    executionListenerSupport.notifyFeatureCompleted(executionToken, feature);
  }

  private void runScenarios(
      ExecutionToken executionToken,
      FeatureToken feature,
      List<ScenarioToken> scenarios,
      HandlerManager handlerManager)
      throws Exception {
    log.debug("Now running scenarios " + scenarios + " for feature " + feature);
    for (Iterator<ScenarioToken> iterator = scenarios.iterator(); iterator.hasNext(); ) {
      ScenarioToken scenario = iterator.next();

      // if the feature start scenario exists and failed we skip all but feature end scenario
      boolean skip =
          !scenario.isFeatureStartScenario()
              && !scenario.isFeatureEndScenario()
              && feature.isFeatureStartScenarioFailed();

      if (skip) {
        log.warn(
            "Skipping scenario "
                + scenario
                + " since "
                + KeyWord.FEATURE_START_SCENARIO_NAME
                + " failed");
      }

      runScenario(executionToken, handlerManager, scenario, skip);
    }
  }

  private void runScenario(
      ExecutionToken executionToken,
      HandlerManager handlerManager,
      ScenarioToken scenario,
      boolean skip)
      throws Exception {
    executionListenerSupport.notifyScenarioStarted(executionToken, scenario);
    log.info(String.format("Processing scenario: %s", scenario.getName()));

    // reset the ChorusContext for the scenario
    ChorusContext.destroy();

    handlerManager.setCurrentScenario(scenario);
    List<Object> handlerInstances = handlerManager.getOrCreateHandlersForScenario();
    handlerManager.processStartOfScope(Scope.SCENARIO, handlerInstances);

    createTimeoutTasks(
        Thread
            .currentThread()); // will interrupt or eventually kill thread / interpreter if blocked

    log.debug("Running scenario steps for Scenario " + scenario);
    StepInvokerProvider p = getStepInvokers(handlerInstances);
    stepProcessor.runSteps(executionToken, p, scenario.getSteps(), skip);

    stopTimeoutTasks();

    // the special start or end scenarios don't count in the execution stats
    if (!scenario.isStartOrEndScenario()) {
      updateExecutionStats(executionToken, scenario);
    }

    handlerManager.processEndOfScope(Scope.SCENARIO, handlerInstances);
    executionListenerSupport.notifyScenarioCompleted(executionToken, scenario);
  }

  private StepInvokerProvider getStepInvokers(List<Object> handlerInstances) {
    CompositeStepInvokerProvider stepInvokerProvider = new CompositeStepInvokerProvider();
    for (Object handler : handlerInstances) {
      HandlerClassInvokerFactory f = new HandlerClassInvokerFactory(handler);
      stepInvokerProvider.addChild(f);
    }
    stepInvokerProvider.addChild((StepInvokerProvider) subsystemManager.getRemotingManager());
    return stepInvokerProvider;
  }

  private void updateExecutionStats(ExecutionToken executionToken, ScenarioToken scenario) {
    if (scenario.getEndState() == EndState.PASSED) {
      executionToken.incrementScenariosPassed();
    } else if (scenario.getEndState() == EndState.PENDING) {
      executionToken.incrementScenariosPending();
    } else {
      executionToken.incrementScenariosFailed();
    }
  }

  private void createTimeoutTasks(final Thread t) {
    scenarioTimeoutInterrupt =
        timeoutExcecutor.schedule(
            new Runnable() {
              public void run() {
                timeoutIfStillRunning(t);
              }
            },
            scenarioTimeoutMillis,
            TimeUnit.MILLISECONDS);

    scenarioTimeoutStopThread =
        timeoutExcecutor.schedule(
            new Runnable() {
              public void run() {
                stopThreadIfStillRunning(t);
              }
            },
            scenarioTimeoutMillis * 2,
            TimeUnit.MILLISECONDS);

    scenarioTimeoutKill =
        timeoutExcecutor.schedule(
            new Runnable() {
              public void run() {
                killInterpreterIfStillRunning(t);
              }
            },
            scenarioTimeoutMillis * 3,
            TimeUnit.MILLISECONDS);
  }

  private void stopTimeoutTasks() {
    scenarioTimeoutInterrupt.cancel(true);
    scenarioTimeoutStopThread.cancel(true);
    scenarioTimeoutKill.cancel(true);
  }

  private void timeoutIfStillRunning(Thread t) {
    if (t.isAlive()) {
      log.warn("Scenario timed out after " + scenarioTimeoutMillis + " millis, will interrupt");
      stepProcessor.setInterruptingOnTimeout(true);
      t.interrupt(); // first try to interrupt to see if this can unblock/fail the scenario
    }
  }

  private void stopThreadIfStillRunning(Thread t) {
    if (t.isAlive()) {
      log.error(
          "Scenario did not respond to interrupt after timeout, "
              + "will stop the interpreter thread and fail the tests");
      t.stop(); // this will trigger a ThreadDeath exception which we should allow to propagate
      // and will terminate the interpreter
    }
  }

  private void killInterpreterIfStillRunning(Thread t) {
    if (t.isAlive()) {
      log.error(
          "Scenario did not respond to thread.kill() after timeout, will now kill the interpreter");
      System.exit(1);
    }
  }

  public void setBasePackages(List<String> basePackages) {
    this.basePackages = basePackages;
  }

  public void setDryRun(boolean dryRun) {
    this.stepProcessor.setDryRun(dryRun);
  }

  public void setScenarioTimeoutMillis(long scenarioTimeoutMillis) {
    this.scenarioTimeoutMillis = scenarioTimeoutMillis;
  }

  public void setSubsystemManager(SubsystemManager subsystemManager) {
    this.subsystemManager = subsystemManager;
  }
}
/**
 * Created with IntelliJ IDEA. User: nick Date: 16/05/12 Time: 17:36 To change this template use
 * File | Settings | File Templates.
 */
public class DynamicProxyMBeanCreator {

  private ChorusLog log = ChorusLogFactory.getLog(DynamicProxyMBeanCreator.class);

  private JMXConnector jmxConnector;
  protected MBeanServerConnection mBeanServerConnection;
  private final String host;
  private final int jmxPort;

  public DynamicProxyMBeanCreator(String host, int jmxPort) {
    this.host = host;
    this.jmxPort = jmxPort;
  }

  public MBeanServerConnection connect() {
    MBeanServerConnection result;
    try {
      String serviceURL =
          String.format("service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", host, jmxPort);
      jmxConnector = JMXConnectorFactory.connect(new JMXServiceURL(serviceURL), null);
      log.debug("Connecting to JMX service URL: " + serviceURL);

      result = jmxConnector.getMBeanServerConnection();

    } catch (Exception e) {
      String msg = String.format("Failed to connect to mBean server on (%s:%s)", host, jmxPort);
      if (log.isDebugEnabled()) {
        log.debug(msg, e);
      } else {
        log.warn(msg);
      }
      throw new ChorusException(msg, e);
    }
    mBeanServerConnection = result;
    return result;
  }

  public <T> T createMBeanProxy(final String mxbeanName, Class<T> mxbeanInterface)
      throws IOException {

    final InvocationHandler handler =
        new InvocationHandler() {

          public Object invoke(Object proxy, final Method method, final Object[] args)
              throws Throwable {
            Class<?>[] paramTypes = method.getParameterTypes();
            if (method.getName().equals("equals")
                && paramTypes.length == 1
                && paramTypes[0] == Object.class) {
              // this is the equals method being called on the proxy, we need to handle it locally
              return args[0] == proxy;
            } else {
              return mBeanServerConnection.invoke(
                  new ObjectName(mxbeanName),
                  method.getName(),
                  args,
                  getClassNameArray(method.getParameterTypes()));
            }
          }

          private String[] getClassNameArray(Class[] args) {
            String[] classNames = new String[args.length];
            int pos = 0;
            for (Class a : args) {
              classNames[pos++] = a.getName();
            }
            return classNames;
          }
        };
    return (T)
        Proxy.newProxyInstance(
            mxbeanInterface.getClassLoader(), new Class[] {mxbeanInterface}, handler);
  }

  // @TODO arrange to close the connection? At what point?
  public void dispose() {
    try {
      jmxConnector.close();
    } catch (IOException e) {
      // safe to ignore this exception - may get here if server process dies before JMX connection
      // is destroyed
    }
  }
}