@Override
  protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
    MessageSelector selector;
    if (AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
      Object target = this.resolveTargetBeanFromMethodWithBeanAnnotation(method);
      if (target instanceof MessageSelector) {
        selector = (MessageSelector) target;
      } else if (this.extractTypeIfPossible(target, MessageFilter.class) != null) {
        checkMessageHandlerAttributes(resolveTargetBeanName(method), annotations);
        return (MessageHandler) target;
      } else {
        selector = new MethodInvokingSelector(target);
      }
    } else {
      Assert.isTrue(
          boolean.class.equals(method.getReturnType())
              || Boolean.class.equals(method.getReturnType()),
          "The Filter annotation may only be applied to methods with a boolean return type.");
      selector = new MethodInvokingSelector(bean, method);
    }

    MessageFilter filter = new MessageFilter(selector);

    String discardWithinAdvice =
        MessagingAnnotationUtils.resolveAttribute(annotations, "discardWithinAdvice", String.class);
    if (StringUtils.hasText(discardWithinAdvice)) {
      discardWithinAdvice = this.environment.resolvePlaceholders(discardWithinAdvice);
      if (StringUtils.hasText(discardWithinAdvice)) {
        filter.setDiscardWithinAdvice(Boolean.parseBoolean(discardWithinAdvice));
      }
    }

    String throwExceptionOnRejection =
        MessagingAnnotationUtils.resolveAttribute(
            annotations, "throwExceptionOnRejection", String.class);
    if (StringUtils.hasText(throwExceptionOnRejection)) {
      String throwExceptionOnRejectionValue =
          this.environment.resolvePlaceholders(throwExceptionOnRejection);
      if (StringUtils.hasText(throwExceptionOnRejectionValue)) {
        filter.setThrowExceptionOnRejection(Boolean.parseBoolean(throwExceptionOnRejectionValue));
      }
    }

    String discardChannelName =
        MessagingAnnotationUtils.resolveAttribute(annotations, "discardChannel", String.class);
    if (StringUtils.hasText(discardChannelName)) {
      filter.setDiscardChannelName(discardChannelName);
    }

    this.setOutputChannelIfPresent(annotations, filter);
    return filter;
  }
 @SuppressWarnings("unchecked")
 protected AbstractEndpoint doCreateEndpoint(
     MessageHandler handler, MessageChannel inputChannel, List<Annotation> annotations) {
   AbstractEndpoint endpoint;
   if (inputChannel instanceof PollableChannel) {
     PollingConsumer pollingConsumer =
         new PollingConsumer((PollableChannel) inputChannel, handler);
     configurePollingEndpoint(pollingConsumer, annotations);
     endpoint = pollingConsumer;
   } else {
     Poller[] pollers =
         MessagingAnnotationUtils.resolveAttribute(annotations, "poller", Poller[].class);
     Assert.state(
         ObjectUtils.isEmpty(pollers),
         "A '@Poller' should not be specified for Annotation-based "
             + "endpoint, since '"
             + inputChannel
             + "' is a SubscribableChannel (not pollable).");
     if (inputChannel instanceof Publisher) {
       Subscriber<Message<?>> subscriber;
       if (handler instanceof Subscriber) {
         subscriber = (Subscriber<Message<?>>) handler;
       } else {
         // TODO errorConsumer, completeConsumer
         DirectProcessor<Message<?>> directProcessor = DirectProcessor.create();
         directProcessor.doOnNext(handler::handleMessage);
         subscriber = directProcessor;
       }
       endpoint = new ReactiveConsumer(inputChannel, subscriber);
     } else {
       endpoint = new EventDrivenConsumer((SubscribableChannel) inputChannel, handler);
     }
   }
   return endpoint;
 }
 protected void setOutputChannelIfPresent(
     List<Annotation> annotations, AbstractReplyProducingMessageHandler handler) {
   String outputChannelName =
       MessagingAnnotationUtils.resolveAttribute(annotations, "outputChannel", String.class);
   if (StringUtils.hasText(outputChannelName)) {
     handler.setOutputChannelName(outputChannelName);
   }
 }
 @Override
 public boolean shouldCreateEndpoint(Method method, List<Annotation> annotations) {
   String inputChannel =
       MessagingAnnotationUtils.resolveAttribute(
           annotations, getInputChannelAttribute(), String.class);
   boolean createEndpoint = StringUtils.hasText(inputChannel);
   if (!createEndpoint && beanAnnotationAware()) {
     boolean isBean = AnnotatedElementUtils.isAnnotated(method, Bean.class.getName());
     Assert.isTrue(
         !isBean,
         "A channel name in '"
             + getInputChannelAttribute()
             + "' is required when "
             + this.annotationType
             + " is used on '@Bean' methods.");
   }
   return createEndpoint;
 }
  protected AbstractEndpoint createEndpoint(
      MessageHandler handler, Method method, List<Annotation> annotations) {
    AbstractEndpoint endpoint = null;
    String inputChannelName =
        MessagingAnnotationUtils.resolveAttribute(
            annotations, getInputChannelAttribute(), String.class);
    if (StringUtils.hasText(inputChannelName)) {
      MessageChannel inputChannel;
      try {
        inputChannel = this.channelResolver.resolveDestination(inputChannelName);
      } catch (DestinationResolutionException e) {
        inputChannel = new DirectChannel();
        this.beanFactory.registerSingleton(inputChannelName, inputChannel);
        inputChannel =
            (MessageChannel) this.beanFactory.initializeBean(inputChannel, inputChannelName);
      }
      Assert.notNull(inputChannel, "failed to resolve inputChannel '" + inputChannelName + "'");

      endpoint = doCreateEndpoint(handler, inputChannel, annotations);
    }
    return endpoint;
  }
 protected void checkMessageHandlerAttributes(
     String handlerBeanName, List<Annotation> annotations) {
   for (String attribute : this.messageHandlerAttributes) {
     for (Annotation annotation : annotations) {
       Object value = AnnotationUtils.getValue(annotation, attribute);
       if (MessagingAnnotationUtils.hasValue(value)) {
         throw new BeanDefinitionValidationException(
             "The MessageHandler ["
                 + handlerBeanName
                 + "] can not"
                 + " be populated because of ambiguity with annotation attributes "
                 + this.messageHandlerAttributes
                 + " which are not allowed when an integration annotation "
                 + "is used with a @Bean definition for a MessageHandler."
                 + "\nThe attribute causing the ambiguity is: ["
                 + attribute
                 + "]."
                 + "\nUse the appropriate setter on the MessageHandler directly when configfuring an "
                 + "endpoint this way.");
       }
     }
   }
 }
 private List<Advice> extractAdviceChain(String beanName, List<Annotation> annotations) {
   List<Advice> adviceChain = null;
   String[] adviceChainNames =
       MessagingAnnotationUtils.resolveAttribute(
           annotations, ADVICE_CHAIN_ATTRIBUTE, String[].class);
   /*
    * Note: we don't merge advice chain contents; if the directAnnotation has a non-empty
    * attribute, it wins. You cannot "remove" an advice chain from a meta-annotation
    * by setting an empty array on the custom annotation.
    */
   if (adviceChainNames != null && adviceChainNames.length > 0) {
     adviceChain = new ArrayList<Advice>();
     for (String adviceChainName : adviceChainNames) {
       Object adviceChainBean = this.beanFactory.getBean(adviceChainName);
       if (adviceChainBean instanceof Advice) {
         adviceChain.add((Advice) adviceChainBean);
       } else if (adviceChainBean instanceof Advice[]) {
         Collections.addAll(adviceChain, (Advice[]) adviceChainBean);
       } else if (adviceChainBean instanceof Collection) {
         @SuppressWarnings("unchecked")
         Collection<Advice> adviceChainEntries = (Collection<Advice>) adviceChainBean;
         for (Advice advice : adviceChainEntries) {
           adviceChain.add(advice);
         }
       } else {
         throw new IllegalArgumentException(
             "Invalid advice chain type:"
                 + adviceChainName.getClass().getName()
                 + " for bean '"
                 + beanName
                 + "'");
       }
     }
   }
   return adviceChain;
 }
  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());
  }
  @Override
  public Object postProcess(
      Object bean, String beanName, Method method, List<Annotation> annotations) {
    if (this.beanAnnotationAware()
        && AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
      try {
        resolveTargetBeanFromMethodWithBeanAnnotation(method);
      } catch (NoSuchBeanDefinitionException e) {
        if (this.logger.isDebugEnabled()) {
          this.logger.debug(
              "Skipping endpoint creation; "
                  + e.getMessage()
                  + "; perhaps due to some '@Conditional' annotation.");
        }
        return null;
      }
    }

    List<Advice> adviceChain = extractAdviceChain(beanName, annotations);

    MessageHandler handler = createHandler(bean, method, annotations);

    if (!CollectionUtils.isEmpty(adviceChain)
        && handler instanceof AbstractReplyProducingMessageHandler) {
      ((AbstractReplyProducingMessageHandler) handler).setAdviceChain(adviceChain);
    }

    if (handler instanceof Orderable) {
      Order orderAnnotation = AnnotationUtils.findAnnotation(method, Order.class);
      if (orderAnnotation != null) {
        ((Orderable) handler).setOrder(orderAnnotation.value());
      }
    }
    if (handler instanceof AbstractMessageProducingHandler
        || handler instanceof AbstractMessageRouter) {
      String sendTimeout =
          MessagingAnnotationUtils.resolveAttribute(annotations, "sendTimeout", String.class);
      if (sendTimeout != null) {
        Long value = Long.valueOf(this.beanFactory.resolveEmbeddedValue(sendTimeout));
        if (handler instanceof AbstractMessageProducingHandler) {
          ((AbstractMessageProducingHandler) handler).setSendTimeout(value);
        } else {
          ((AbstractMessageRouter) handler).setSendTimeout(value);
        }
      }
    }

    boolean handlerExists = false;
    if (this.beanAnnotationAware()
        && AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
      Object handlerBean = this.resolveTargetBeanFromMethodWithBeanAnnotation(method);
      handlerExists = handlerBean != null && handler == handlerBean;
    }

    if (!handlerExists) {
      String handlerBeanName = generateHandlerBeanName(beanName, method);
      this.beanFactory.registerSingleton(handlerBeanName, handler);
      handler = (MessageHandler) this.beanFactory.initializeBean(handler, handlerBeanName);
    }

    if (AnnotatedElementUtils.isAnnotated(method, IdempotentReceiver.class.getName())
        && !AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
      String[] interceptors =
          AnnotationUtils.getAnnotation(method, IdempotentReceiver.class).value();
      for (String interceptor : interceptors) {
        DefaultBeanFactoryPointcutAdvisor advisor = new DefaultBeanFactoryPointcutAdvisor();
        advisor.setAdviceBeanName(interceptor);
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.setMappedName("handleMessage");
        advisor.setPointcut(pointcut);
        advisor.setBeanFactory(this.beanFactory);

        if (handler instanceof Advised) {
          ((Advised) handler).addAdvisor(advisor);
        } else {
          ProxyFactory proxyFactory = new ProxyFactory(handler);
          proxyFactory.addAdvisor(advisor);
          handler = (MessageHandler) proxyFactory.getProxy(this.beanFactory.getBeanClassLoader());
        }
      }
    }

    if (!CollectionUtils.isEmpty(adviceChain)) {
      for (Advice advice : adviceChain) {
        if (advice instanceof HandleMessageAdvice) {
          NameMatchMethodPointcutAdvisor handlerAdvice = new NameMatchMethodPointcutAdvisor(advice);
          handlerAdvice.addMethodName("handleMessage");
          if (handler instanceof Advised) {
            ((Advised) handler).addAdvisor(handlerAdvice);
          } else {
            ProxyFactory proxyFactory = new ProxyFactory(handler);
            proxyFactory.addAdvisor(handlerAdvice);
            handler = (MessageHandler) proxyFactory.getProxy(this.beanFactory.getBeanClassLoader());
          }
        }
      }
    }

    AbstractEndpoint endpoint = createEndpoint(handler, method, annotations);
    if (endpoint != null) {
      return endpoint;
    }
    return handler;
  }