/** This is run before each @TestD method. */
  @Before
  public void before() {
    if (!run) {
      AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
      bot =
          (MyBot)
              beanFactory.autowire(MyBot.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
      Object botBean = beanFactory.initializeBean(bot, "mybot");
      bot = (MyBot) botBean;
      // the bot also needs a trigger so its act() method is called regularly.
      // (there is no trigger bean in the context)
      PeriodicTrigger trigger = new PeriodicTrigger(ACT_LOOP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
      trigger.setInitialDelay(ACT_LOOP_INITIAL_DELAY_MILLIS);
      bot.setTrigger(trigger);
      logger.info("starting test case testGroupBot");
      // adding the bot to the bot manager will cause it to be initialized.
      // at that point, the trigger starts.
      botManager.addBot(bot);

      // the bot should now be running. We have to wait for it to finish before we
      // can check the results:
      // Together with the barrier.await() in the bot's listener, this trips the barrier
      // and both threads continue.
      try {
        bot.getBarrier().await();
      } catch (InterruptedException e) {
        e.printStackTrace(); // To change body of catch statement use File | Settings | File
        // Templates.
      } catch (BrokenBarrierException e) {
        e.printStackTrace(); // To change body of catch statement use File | Settings | File
        // Templates.
      }
      run = true;
    }
  }
  /** Starts session validation by creating a spring PeriodicTrigger. */
  public void enableSessionValidation() {

    enabled = true;

    if (log.isDebugEnabled()) {
      log.debug(
          "Scheduling session validation job using Spring Scheduler with "
              + "session validation interval of ["
              + sessionValidationInterval
              + "]ms...");
    }

    try {

      PeriodicTrigger trigger =
          new PeriodicTrigger(sessionValidationInterval, TimeUnit.MILLISECONDS);
      trigger.setInitialDelay(sessionValidationInterval);

      scheduler.schedule(
          new Runnable() {
            @Override
            public void run() {
              if (enabled) {
                sessionManager.validateSessions();
              }
            }
          },
          trigger);

      this.enabled = true;

      if (log.isDebugEnabled()) {
        log.debug("Session validation job successfully scheduled with Spring Scheduler.");
      }

    } catch (Exception e) {
      if (log.isErrorEnabled()) {
        log.error(
            "Error starting the Spring Scheduler session validation job.  Session validation may not occur.",
            e);
      }
    }
  }
  /**
   * Processes a new {@link Trigger} being added. Currently, it supports adding {@link
   * CronTrigger}s. The {@link Trigger} is added to the common {@link
   * ConfigurableApplicationContext}.
   */
  @Override
  public void preProcessModule(Module module) {
    if (!TRIGGER.equals(module.getType())) {
      return;
    }
    Assert.notNull(
        commonApplicationContext, "The 'commonApplicationContext' property must not be null.");

    Map<String, Trigger> triggers = new HashMap<String, Trigger>();
    if (module.getProperties().containsKey(TriggerType.cron.name())) {
      Trigger trigger =
          new CronTrigger(module.getProperties().getProperty(TriggerType.cron.name()));
      triggers.put(TriggerType.cron.name(), trigger);
    }
    if (module.getProperties().containsKey(TriggerType.fixedDelay.name())) {
      Trigger trigger =
          new PeriodicTrigger(
              Long.parseLong(module.getProperties().getProperty(TriggerType.fixedDelay.name())));
      triggers.put(TriggerType.fixedDelay.name(), trigger);
    }
    if (module.getProperties().containsKey(TriggerType.fixedRate.name())) {
      PeriodicTrigger trigger =
          new PeriodicTrigger(
              Long.parseLong(module.getProperties().getProperty(TriggerType.fixedRate.name())));
      trigger.setFixedRate(true);
      triggers.put(TriggerType.fixedRate.name(), trigger);
    }
    if (triggers.size() == 0) {
      throw new ResourceDefinitionException(
          "No valid trigger property. Expected one of: cron, fixedDelay or fixedRate");
    } else if (triggers.size() > 1) {
      throw new ResourceDefinitionException(
          "Only one trigger property allowed, but received: "
              + StringUtils.collectionToCommaDelimitedString(triggers.keySet()));
    }
    commonApplicationContext
        .getBeanFactory()
        .registerSingleton(makeTriggerBeanName(module), triggers.values().iterator().next());
    configureProperties(module);
  }
  protected void configurePollingEndpoint(
      AbstractPollingEndpoint pollingEndpoint, List<Annotation> annotations) {
    PollerMetadata pollerMetadata = null;
    Poller[] pollers =
        MessagingAnnotationUtils.resolveAttribute(annotations, "poller", Poller[].class);
    if (!ObjectUtils.isEmpty(pollers)) {
      Assert.state(
          pollers.length == 1,
          "The 'poller' for an Annotation-based endpoint can have only one '@Poller'.");
      Poller poller = pollers[0];

      String ref = poller.value();
      String triggerRef = poller.trigger();
      String executorRef = poller.taskExecutor();
      String fixedDelayValue = this.beanFactory.resolveEmbeddedValue(poller.fixedDelay());
      String fixedRateValue = this.beanFactory.resolveEmbeddedValue(poller.fixedRate());
      String maxMessagesPerPollValue =
          this.beanFactory.resolveEmbeddedValue(poller.maxMessagesPerPoll());
      String cron = this.beanFactory.resolveEmbeddedValue(poller.cron());
      String errorChannel = this.beanFactory.resolveEmbeddedValue(poller.errorChannel());

      if (StringUtils.hasText(ref)) {
        Assert.state(
            !StringUtils.hasText(triggerRef)
                && !StringUtils.hasText(executorRef)
                && !StringUtils.hasText(cron)
                && !StringUtils.hasText(fixedDelayValue)
                && !StringUtils.hasText(fixedRateValue)
                && !StringUtils.hasText(maxMessagesPerPollValue),
            "The '@Poller' 'ref' attribute is mutually exclusive with other attributes.");
        pollerMetadata = this.beanFactory.getBean(ref, PollerMetadata.class);
      } else {
        pollerMetadata = new PollerMetadata();
        if (StringUtils.hasText(maxMessagesPerPollValue)) {
          pollerMetadata.setMaxMessagesPerPoll(Long.parseLong(maxMessagesPerPollValue));
        } else if (pollingEndpoint instanceof SourcePollingChannelAdapter) {
          // SPCAs default to 1 message per poll
          pollerMetadata.setMaxMessagesPerPoll(1);
        }

        if (StringUtils.hasText(executorRef)) {
          pollerMetadata.setTaskExecutor(this.beanFactory.getBean(executorRef, TaskExecutor.class));
        }

        Trigger trigger = null;
        if (StringUtils.hasText(triggerRef)) {
          Assert.state(
              !StringUtils.hasText(cron)
                  && !StringUtils.hasText(fixedDelayValue)
                  && !StringUtils.hasText(fixedRateValue),
              "The '@Poller' 'trigger' attribute is mutually exclusive with other attributes.");
          trigger = this.beanFactory.getBean(triggerRef, Trigger.class);
        } else if (StringUtils.hasText(cron)) {
          Assert.state(
              !StringUtils.hasText(fixedDelayValue) && !StringUtils.hasText(fixedRateValue),
              "The '@Poller' 'cron' attribute is mutually exclusive with other attributes.");
          trigger = new CronTrigger(cron);
        } else if (StringUtils.hasText(fixedDelayValue)) {
          Assert.state(
              !StringUtils.hasText(fixedRateValue),
              "The '@Poller' 'fixedDelay' attribute is mutually exclusive with other attributes.");
          trigger = new PeriodicTrigger(Long.parseLong(fixedDelayValue));
        } else if (StringUtils.hasText(fixedRateValue)) {
          trigger = new PeriodicTrigger(Long.parseLong(fixedRateValue));
          ((PeriodicTrigger) trigger).setFixedRate(true);
        }
        // 'Trigger' can be null. 'PollingConsumer' does fallback to the 'new PeriodicTrigger(10)'.
        pollerMetadata.setTrigger(trigger);

        if (StringUtils.hasText(errorChannel)) {
          MessagePublishingErrorHandler errorHandler = new MessagePublishingErrorHandler();
          errorHandler.setDefaultErrorChannelName(errorChannel);
          errorHandler.setBeanFactory(this.beanFactory);
          pollerMetadata.setErrorHandler(errorHandler);
        }
      }
    } else {
      pollerMetadata = PollerMetadata.getDefaultPollerMetadata(this.beanFactory);
      Assert.notNull(
          pollerMetadata,
          "No poller has been defined for Annotation-based endpoint, "
              + "and no default poller is available within the context.");
    }
    pollingEndpoint.setTaskExecutor(pollerMetadata.getTaskExecutor());
    pollingEndpoint.setTrigger(pollerMetadata.getTrigger());
    pollingEndpoint.setAdviceChain(pollerMetadata.getAdviceChain());
    pollingEndpoint.setMaxMessagesPerPoll(pollerMetadata.getMaxMessagesPerPoll());
    pollingEndpoint.setErrorHandler(pollerMetadata.getErrorHandler());
    if (pollingEndpoint instanceof PollingConsumer) {
      ((PollingConsumer) pollingEndpoint).setReceiveTimeout(pollerMetadata.getReceiveTimeout());
    }
    pollingEndpoint.setTransactionSynchronizationFactory(
        pollerMetadata.getTransactionSynchronizationFactory());
  }
  protected Trigger tryExpressionAsPeriodicTrigger(String configValue)
      throws BadTriggerConfigException {
    BadTriggerConfigException longParseException = null;
    try {
      final long period = Long.parseLong(configValue);
      if (period < 0) {
        return new DisabledTrigger();
      }
      // millis since that's what @Scheduled methods were historically
      // configured in
      return new PeriodicTrigger(period, TimeUnit.MILLISECONDS);
    } catch (NumberFormatException e) {
      longParseException =
          new BadTriggerConfigException(
              "Config [" + configValue + "] did not parse to a long.", configValue, e);
    } catch (IllegalArgumentException e) {
      longParseException =
          new BadTriggerConfigException(
              "Config [" + configValue + "] could not be used to initialize a PeriodicTrigger",
              configValue,
              e);
    }

    final Matcher matcher = PERIODIC_TRIGGER_WITH_INITIAL_DELAY_PATTERN.matcher(configValue);
    if (!(matcher.matches())) {
      throw new BadTriggerConfigException(
          "Trigger expression could not be parsed as either a"
              + " simple period or as a period with an initial "
              + "offset. Original parse failure: ["
              + longParseException.getMessage()
              + "]. To be considered a period with an offset, "
              + "the expression must match this regexp"
              + "(without brackets): ["
              + PERIODIC_TRIGGER_WITH_INITIAL_DELAY_PATTERN
              + "]",
          configValue);
    }

    try {
      final String periodStr = matcher.group(1);
      final long periodLong = Long.parseLong(periodStr);

      if (periodLong < 0) {
        return new DisabledTrigger();
      }

      final String offsetStr = matcher.group(2);
      final long offsetLong = Long.parseLong(offsetStr);

      final PeriodicTrigger trigger = new PeriodicTrigger(periodLong, TimeUnit.MILLISECONDS);
      trigger.setInitialDelay(offsetLong);
      return trigger;
    } catch (NumberFormatException e) {
      throw new BadTriggerConfigException(
          "Trigger expression could not be parsed as either a"
              + " simple period or as a period with an initial "
              + "offset. Original parse failure: ["
              + longParseException.getMessage()
              + "]. To be considered a period with an initial "
              + "delay, the expression must match this regexp"
              + "(without brackets): ["
              + PERIODIC_TRIGGER_WITH_INITIAL_DELAY_PATTERN
              + "]",
          configValue,
          e);
    } catch (IllegalArgumentException e) {
      throw new BadTriggerConfigException(
          "Trigger expression parsed as a period [{}] with an "
              + "initial delay [{}] but could not be used to "
              + "initialize a PeriodicTrigger",
          configValue,
          e);
    }
  }