/**
  * Builds and configures a {@link BeanDefinitionBuilder} for {@link
  * HdfsStoreMessageHandlerFactoryBean}.
  *
  * @param element The element.
  * @param parserContext The parser context.
  * @return The bean definition builder
  */
 static BeanDefinitionBuilder configure(Element element, ParserContext parserContext) {
   BeanDefinitionBuilder builder =
       BeanDefinitionBuilder.genericBeanDefinition(HdfsStoreMessageHandlerFactoryBean.class);
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
       builder, element, "writer", "storeWriter");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
   return builder;
 }
  @Override
  protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
    BeanDefinitionBuilder builder =
        BeanDefinitionBuilder.genericBeanDefinition(
            "org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler");
    builder.addConstructorArgValue(element.getAttribute("url"));
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "http-method");

    String restTemplate = element.getAttribute("rest-template");
    if (StringUtils.hasText(restTemplate)) {
      HttpAdapterParsingUtils.verifyNoRestTemplateAttributes(element, parserContext);
      builder.addConstructorArgReference(restTemplate);
    } else {
      for (String referenceAttributeName :
          HttpAdapterParsingUtils.REST_TEMPLATE_REFERENCE_ATTRIBUTES) {
        IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
            builder, element, referenceAttributeName);
      }
    }

    String headerMapper = element.getAttribute("header-mapper");
    String mappedRequestHeaders = element.getAttribute("mapped-request-headers");
    String mappedResponseHeaders = element.getAttribute("mapped-response-headers");
    if (StringUtils.hasText(headerMapper)) {
      if (StringUtils.hasText(mappedRequestHeaders) || StringUtils.hasText(mappedResponseHeaders)) {
        parserContext
            .getReaderContext()
            .error(
                "Neither 'mappped-request-headers' or 'mapped-response-headers' "
                    + "attributes are allowed when a 'header-mapper' has been specified.",
                parserContext.extractSource(element));
        return null;
      }
      builder.addPropertyReference("headerMapper", headerMapper);
    } else {
      BeanDefinitionBuilder headerMapperBuilder =
          BeanDefinitionBuilder.genericBeanDefinition(
              "org.springframework.integration.http.support.DefaultHttpHeaderMapper");
      headerMapperBuilder.setFactoryMethod("outboundMapper");
      IntegrationNamespaceUtils.setValueIfAttributeDefined(
          headerMapperBuilder, element, "mapped-request-headers", "outboundHeaderNames");
      IntegrationNamespaceUtils.setValueIfAttributeDefined(
          headerMapperBuilder, element, "mapped-response-headers", "inboundHeaderNames");
      builder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition());
    }
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        builder, element, "extract-request-payload", "extractPayload");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        builder, element, "expected-response-type");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        builder, element, "reply-timeout", "sendTimeout");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
        builder, element, "reply-channel", "outputChannel");
    HttpAdapterParsingUtils.configureUriVariableExpressions(builder, element);
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "transfer-cookies");
    return builder;
  }
  /**
   * Create a new {@link BeanDefinitionBuilder} for the class {@link StoredProcExecutor}. Initialize
   * the wrapped {@link StoredProcExecutor} with common properties.
   *
   * @param element Must not be Null
   * @param parserContext Must not be Null
   * @return The {@link BeanDefinitionBuilder} for the {@link StoredProcExecutor}
   */
  public static BeanDefinitionBuilder getStoredProcExecutorBuilder(
      final Element element, final ParserContext parserContext) {

    Assert.notNull(element, "The provided element must not be Null.");
    Assert.notNull(parserContext, "The provided parserContext must not be Null.");

    final String dataSourceRef = element.getAttribute("data-source");

    final BeanDefinitionBuilder storedProcExecutorBuilder =
        BeanDefinitionBuilder.genericBeanDefinition(StoredProcExecutor.class);
    storedProcExecutorBuilder.addConstructorArgReference(dataSourceRef);

    final String storedProcedureName = element.getAttribute("stored-procedure-name");
    final String storedProcedureNameExpression =
        element.getAttribute("stored-procedure-name-expression");
    boolean hasStoredProcedureName = StringUtils.hasText(storedProcedureName);
    boolean hasStoredProcedureNameExpression = StringUtils.hasText(storedProcedureNameExpression);

    if (!(hasStoredProcedureName ^ hasStoredProcedureNameExpression)) {
      parserContext
          .getReaderContext()
          .error(
              "Exactly one of 'stored-procedure-name' or 'stored-procedure-name-expression' is required",
              element);
    }

    BeanDefinitionBuilder expressionBuilder;
    if (hasStoredProcedureNameExpression) {
      expressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class);
      expressionBuilder.addConstructorArgValue(storedProcedureNameExpression);
    } else {
      expressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(LiteralExpression.class);
      expressionBuilder.addConstructorArgValue(storedProcedureName);
    }
    storedProcExecutorBuilder.addPropertyValue(
        "storedProcedureNameExpression", expressionBuilder.getBeanDefinition());

    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        storedProcExecutorBuilder, element, "ignore-column-meta-data");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        storedProcExecutorBuilder, element, "jdbc-call-operations-cache-size");

    final ManagedList<BeanDefinition> procedureParameterList =
        StoredProcParserUtils.getProcedureParameterBeanDefinitions(element, parserContext);
    final ManagedList<BeanDefinition> sqlParameterDefinitionList =
        StoredProcParserUtils.getSqlParameterDefinitionBeanDefinitions(element, parserContext);

    if (!procedureParameterList.isEmpty()) {
      storedProcExecutorBuilder.addPropertyValue("procedureParameters", procedureParameterList);
    }
    if (!sqlParameterDefinitionList.isEmpty()) {
      storedProcExecutorBuilder.addPropertyValue("sqlParameters", sqlParameterDefinitionList);
    }

    return storedProcExecutorBuilder;
  }
  @Override
  protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
    final BeanDefinitionBuilder builder =
        BeanDefinitionBuilder.genericBeanDefinition(ContentEnricher.class);

    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-channel");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");

    List<Element> subElements = DomUtils.getChildElementsByTagName(element, "property");
    if (!CollectionUtils.isEmpty(subElements)) {
      ManagedMap<String, Object> expressions = new ManagedMap<String, Object>();
      for (Element subElement : subElements) {
        String name = subElement.getAttribute("name");
        BeanDefinition beanDefinition =
            IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression(
                "value", "expression", parserContext, subElement, true);
        expressions.put(name, beanDefinition);
      }
      builder.addPropertyValue("propertyExpressions", expressions);
    }

    subElements = DomUtils.getChildElementsByTagName(element, "header");
    if (!CollectionUtils.isEmpty(subElements)) {
      ManagedMap<String, Object> expressions = new ManagedMap<String, Object>();
      for (Element subElement : subElements) {
        String name = subElement.getAttribute("name");
        BeanDefinition expressionDefinition =
            IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression(
                "value", "expression", parserContext, subElement, true);
        BeanDefinitionBuilder valueProcessorBuilder =
            BeanDefinitionBuilder.genericBeanDefinition(
                IntegrationNamespaceUtils.BASE_PACKAGE
                    + ".transformer.support.ExpressionEvaluatingHeaderValueMessageProcessor");
        valueProcessorBuilder
            .addConstructorArgValue(expressionDefinition)
            .addConstructorArgValue(subElement.getAttribute("type"));
        IntegrationNamespaceUtils.setValueIfAttributeDefined(
            valueProcessorBuilder, subElement, "overwrite");
        expressions.put(name, valueProcessorBuilder.getBeanDefinition());
      }
      builder.addPropertyValue("headerExpressions", expressions);
    }

    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "should-clone-payload");

    String requestPayloadExpression = element.getAttribute("request-payload-expression");

    if (StringUtils.hasText(requestPayloadExpression)) {
      BeanDefinitionBuilder expressionBuilder =
          BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class);
      expressionBuilder.addConstructorArgValue(requestPayloadExpression);
      builder.addPropertyValue("requestPayloadExpression", expressionBuilder.getBeanDefinition());
    }

    return builder;
  }
  private void configureRequestHandlerAdviceChain(
      Element element,
      ParserContext parserContext,
      BeanDefinition handlerBeanDefinition,
      BeanDefinitionBuilder consumerBuilder) {
    Element adviceChainElement =
        DomUtils.getChildElementByTagName(
            element, IntegrationNamespaceUtils.REQUEST_HANDLER_ADVICE_CHAIN);
    @SuppressWarnings("rawtypes")
    ManagedList adviceChain =
        IntegrationNamespaceUtils.configureAdviceChain(
            adviceChainElement, null, handlerBeanDefinition, parserContext);
    if (adviceChain != null) {
      /*
       * For ARPMH, the advice chain is injected so just the handleRequestMessage method is advised.
       * Sometime ARPMHs do double duty as a gateway and a channel adapter. The parser subclass
       * can indicate this by overriding isUsingReplyProducer(), or we can try to determine it from
       * the bean class.
       */
      boolean isReplyProducer = this.isUsingReplyProducer();
      if (!isReplyProducer) {
        Class<?> beanClass = null;
        if (handlerBeanDefinition instanceof AbstractBeanDefinition) {
          AbstractBeanDefinition abstractBeanDefinition =
              (AbstractBeanDefinition) handlerBeanDefinition;
          if (abstractBeanDefinition.hasBeanClass()) {
            beanClass = abstractBeanDefinition.getBeanClass();
          }
        }
        isReplyProducer =
            beanClass != null
                && AbstractReplyProducingMessageHandler.class.isAssignableFrom(beanClass);
      }

      if (isReplyProducer) {
        handlerBeanDefinition.getPropertyValues().add("adviceChain", adviceChain);
      } else if (consumerBuilder != null) {
        consumerBuilder.addPropertyValue("adviceChain", adviceChain);
      } else {
        String elementDescription = IntegrationNamespaceUtils.createElementDescription(element);
        parserContext
            .getReaderContext()
            .error(
                "'request-handler-advice-chain' isn't allowed for "
                    + elementDescription
                    + " within a <chain/>, because its Handler "
                    + "isn't an AbstractReplyProducingMessageHandler",
                element);
      }
    }
  }
  @Override
  protected AbstractBeanDefinition doParse(
      Element element, ParserContext parserContext, String channelName) {
    BeanDefinitionBuilder builder =
        ReactorNamespaceUtils.createBeanDefinitionBuilder(
            SyslogInboundChannelAdapter.class, element);

    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "host", "host");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "port", "port");

    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
        builder, element, "channel", "outputChannel");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
        builder, element, "error-channel", "errorChannel");

    return builder.getBeanDefinition();
  }
  @Override
  protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
    BeanDefinitionBuilder builder =
        BeanDefinitionBuilder.genericBeanDefinition(RedisCollectionPopulatingMessageHandler.class);

    String redisTemplateRef = element.getAttribute("redis-template");
    String connectionFactory = element.getAttribute("connection-factory");
    if (StringUtils.hasText(redisTemplateRef) && StringUtils.hasText(connectionFactory)) {
      parserContext
          .getReaderContext()
          .error("Only one of 'redis-template' or 'connection-factory'" + " is allowed", element);
    }

    if (StringUtils.hasText(redisTemplateRef)) {
      builder.addConstructorArgReference(redisTemplateRef);
    } else {
      if (!StringUtils.hasText(connectionFactory)) {
        connectionFactory = "redisConnectionFactory";
      }
      builder.addConstructorArgReference(connectionFactory);
    }

    boolean atLeastOneRequired = false;
    RootBeanDefinition expressionDef =
        IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression(
            "key", "key-expression", parserContext, element, atLeastOneRequired);
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "collection-type");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        builder, element, "extract-payload-elements");

    if (expressionDef != null) {
      builder.addConstructorArgValue(expressionDef);
    }

    String mapKeyExpression = element.getAttribute("map-key-expression");
    if (StringUtils.hasText(mapKeyExpression)) {
      RootBeanDefinition mapKeyExpressionDef = new RootBeanDefinition(ExpressionFactoryBean.class);
      mapKeyExpressionDef.getConstructorArgumentValues().addGenericArgumentValue(mapKeyExpression);
      builder.addPropertyValue("mapKeyExpression", mapKeyExpressionDef);
    }

    return builder.getBeanDefinition();
  }
  @Override
  protected AbstractBeanDefinition doParse(
      Element element, ParserContext parserContext, String channelName) {
    if (parserContext.isNested()) {
      if (channelName != null) {
        String elementDescription = IntegrationNamespaceUtils.createElementDescription(element);
        parserContext
            .getReaderContext()
            .error(
                "The 'channel' attribute isn't allowed for "
                    + elementDescription
                    + " when it is used as a nested element,"
                    + " e.g. inside a <chain/>",
                element);
      }
      AbstractBeanDefinition consumerBeanDefinition = this.parseConsumer(element, parserContext);
      this.configureRequestHandlerAdviceChain(element, parserContext, consumerBeanDefinition, null);
      return consumerBeanDefinition;
    }
    BeanDefinitionBuilder builder =
        BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class);
    Element pollerElement = DomUtils.getChildElementByTagName(element, "poller");
    BeanComponentDefinition handlerBeanComponentDefinition =
        this.doParseAndRegisterConsumer(element, parserContext);
    builder.addPropertyReference("handler", handlerBeanComponentDefinition.getBeanName());
    IntegrationNamespaceUtils.checkAndConfigureFixedSubscriberChannel(
        element, parserContext, channelName, handlerBeanComponentDefinition.getBeanName());
    if (pollerElement != null) {
      if (!StringUtils.hasText(channelName)) {
        parserContext
            .getReaderContext()
            .error(
                "outbound channel adapter with a 'poller' requires a 'channel' to poll", element);
      }
      IntegrationNamespaceUtils.configurePollerMetadata(pollerElement, builder, parserContext);
    }
    builder.addPropertyValue("inputChannelName", channelName);

    this.configureRequestHandlerAdviceChain(
        element, parserContext, handlerBeanComponentDefinition.getBeanDefinition(), builder);

    return builder.getBeanDefinition();
  }
  @Override
  protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {

    BeanDefinitionBuilder builder =
        BeanDefinitionBuilder.genericBeanDefinition(StoredProcPollingChannelAdapter.class);

    final BeanDefinitionBuilder storedProcExecutorBuilder =
        StoredProcParserUtils.getStoredProcExecutorBuilder(element, parserContext);

    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        storedProcExecutorBuilder, element, "return-value-required");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        storedProcExecutorBuilder, element, "function");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        storedProcExecutorBuilder, element, "skip-undeclared-results");

    final ManagedMap<String, BeanDefinition> returningResultsetMap =
        StoredProcParserUtils.getReturningResultsetBeanDefinitions(element, parserContext);

    if (!returningResultsetMap.isEmpty()) {
      storedProcExecutorBuilder.addPropertyValue(
          "returningResultSetRowMappers", returningResultsetMap);
    }

    final AbstractBeanDefinition storedProcExecutorBuilderBeanDefinition =
        storedProcExecutorBuilder.getBeanDefinition();
    final String storedProcPollingChannelAdapterId =
        this.resolveId(element, builder.getRawBeanDefinition(), parserContext);
    final String storedProcExecutorBeanName =
        storedProcPollingChannelAdapterId + ".storedProcExecutor";

    parserContext.registerBeanComponent(
        new BeanComponentDefinition(
            storedProcExecutorBuilderBeanDefinition, storedProcExecutorBeanName));

    builder.addConstructorArgReference(storedProcExecutorBeanName);

    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "expect-single-result");

    return builder.getBeanDefinition();
  }
 @Override
 protected AbstractBeanDefinition doParse(
     Element element, ParserContext parserContext, String channelName) {
   BeanMetadataElement source = this.parseSource(element, parserContext);
   if (source == null) {
     parserContext.getReaderContext().error("failed to parse source", element);
   }
   BeanDefinitionBuilder adapterBuilder =
       BeanDefinitionBuilder.genericBeanDefinition(SourcePollingChannelAdapterFactoryBean.class);
   adapterBuilder.addPropertyValue("source", source);
   adapterBuilder.addPropertyReference("outputChannel", channelName);
   IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "send-timeout");
   Element pollerElement = DomUtils.getChildElementByTagName(element, "poller");
   if (pollerElement != null) {
     IntegrationNamespaceUtils.configurePollerMetadata(
         pollerElement, adapterBuilder, parserContext);
   }
   IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "auto-startup");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "phase");
   return adapterBuilder.getBeanDefinition();
 }
  @Override
  protected BeanDefinitionBuilder buildBeanDefinition(
      Element element, ParserContext parserContext) {
    BeanDefinitionBuilder builder =
        BeanDefinitionBuilder.genericBeanDefinition(SubscribableRedisChannel.class);
    String connectionFactory = element.getAttribute("connection-factory");
    if (!StringUtils.hasText(connectionFactory)) {
      connectionFactory = "redisConnectionFactory";
    }
    builder.addConstructorArgReference(connectionFactory);
    String topicName = element.getAttribute("topic-name");
    builder.addConstructorArgValue(topicName);

    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "task-executor");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-converter");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "serializer");
    // The following 2 attributes should be added once configurable on the
    // RedisMessageListenerContainer
    // IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "phase");
    // IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
    return builder;
  }
  @Override
  protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {

    final BeanDefinitionBuilder jpaPollingChannelAdapterBuilder =
        BeanDefinitionBuilder.genericBeanDefinition(JpaPollingChannelAdapter.class);

    final BeanDefinitionBuilder jpaExecutorBuilder =
        JpaParserUtils.getJpaExecutorBuilder(element, parserContext);

    String maxNumberOfResults = element.getAttribute("max-number-of-results");
    boolean hasMaxNumberOfResults = StringUtils.hasText(maxNumberOfResults);

    String maxResults = element.getAttribute("max-results");
    boolean hasMaxResults = StringUtils.hasText(maxResults);

    if (hasMaxNumberOfResults) {
      parserContext
          .getReaderContext()
          .warning("'max-number-of-results' is deprecated in favor of 'max-results'", element);
      if (hasMaxResults) {
        parserContext
            .getReaderContext()
            .error("'max-number-of-results' and 'max-results' are mutually exclusive", element);
      } else {
        element.setAttribute("max-results", maxNumberOfResults);
      }
    }

    BeanDefinition definition =
        IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression(
            "max-results", "max-results-expression", parserContext, element, false);
    if (definition != null) {
      jpaExecutorBuilder.addPropertyValue("maxResultsExpression", definition);
    }
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        jpaExecutorBuilder, element, "delete-after-poll");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        jpaExecutorBuilder, element, "flush-after-delete", "flush");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        jpaExecutorBuilder, element, "delete-in-batch");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        jpaExecutorBuilder, element, "expect-single-result");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
        jpaExecutorBuilder, element, "parameter-source");

    final BeanDefinition jpaExecutorBuilderBeanDefinition = jpaExecutorBuilder.getBeanDefinition();
    final String channelAdapterId =
        this.resolveId(
            element, jpaPollingChannelAdapterBuilder.getRawBeanDefinition(), parserContext);
    final String jpaExecutorBeanName = channelAdapterId + ".jpaExecutor";

    parserContext.registerBeanComponent(
        new BeanComponentDefinition(jpaExecutorBuilderBeanDefinition, jpaExecutorBeanName));

    jpaPollingChannelAdapterBuilder.addConstructorArgReference(jpaExecutorBeanName);

    return jpaPollingChannelAdapterBuilder.getBeanDefinition();
  }
 @SuppressWarnings({"rawtypes", "unchecked"})
 public static ManagedList configureAdviceChain(
     Element adviceChainElement,
     Element txElement,
     BeanDefinition parentBeanDefinition,
     ParserContext parserContext) {
   ManagedList adviceChain = null;
   // Schema validation ensures txElement and adviceChainElement are mutually exclusive
   if (txElement != null) {
     adviceChain = new ManagedList();
     adviceChain.add(IntegrationNamespaceUtils.configureTransactionAttributes(txElement));
   }
   if (adviceChainElement != null) {
     adviceChain = new ManagedList();
     NodeList childNodes = adviceChainElement.getChildNodes();
     for (int i = 0; i < childNodes.getLength(); i++) {
       Node child = childNodes.item(i);
       if (child.getNodeType() == Node.ELEMENT_NODE) {
         Element childElement = (Element) child;
         String localName = child.getLocalName();
         if ("bean".equals(localName)) {
           BeanDefinitionHolder holder =
               parserContext
                   .getDelegate()
                   .parseBeanDefinitionElement(childElement, parentBeanDefinition);
           parserContext.registerBeanComponent(new BeanComponentDefinition(holder));
           adviceChain.add(new RuntimeBeanReference(holder.getBeanName()));
         } else if ("ref".equals(localName)) {
           String ref = childElement.getAttribute("bean");
           adviceChain.add(new RuntimeBeanReference(ref));
         } else {
           BeanDefinition customBeanDefinition =
               parserContext.getDelegate().parseCustomElement(childElement, parentBeanDefinition);
           if (customBeanDefinition == null) {
             parserContext
                 .getReaderContext()
                 .error("failed to parse custom element '" + localName + "'", childElement);
           }
           adviceChain.add(customBeanDefinition);
         }
       }
     }
   }
   return adviceChain;
 }
 @Override
 protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
   BeanDefinitionBuilder builder =
       BeanDefinitionBuilder.genericBeanDefinition(RequestHandlerRetryAdvice.class);
   BeanDefinitionBuilder retryTemplateBuilder =
       BeanDefinitionBuilder.genericBeanDefinition(RetryTemplate.class);
   boolean customTemplate = false;
   Element backOffPolicyEle = DomUtils.getChildElementByTagName(element, "fixed-back-off");
   BeanDefinitionBuilder backOffBuilder = null;
   if (backOffPolicyEle != null) {
     backOffBuilder = BeanDefinitionBuilder.genericBeanDefinition(FixedBackOffPolicy.class);
     IntegrationNamespaceUtils.setValueIfAttributeDefined(
         backOffBuilder, backOffPolicyEle, "interval", "backOffPeriod");
   } else {
     backOffPolicyEle = DomUtils.getChildElementByTagName(element, "exponential-back-off");
     if (backOffPolicyEle != null) {
       backOffBuilder =
           BeanDefinitionBuilder.genericBeanDefinition(ExponentialBackOffPolicy.class);
       IntegrationNamespaceUtils.setValueIfAttributeDefined(
           backOffBuilder, backOffPolicyEle, "initial", "initialInterval");
       IntegrationNamespaceUtils.setValueIfAttributeDefined(
           backOffBuilder, backOffPolicyEle, "multiplier");
       IntegrationNamespaceUtils.setValueIfAttributeDefined(
           backOffBuilder, backOffPolicyEle, "maximum", "maxInterval");
     }
   }
   if (backOffBuilder != null) {
     retryTemplateBuilder.addPropertyValue("backOffPolicy", backOffBuilder.getBeanDefinition());
     customTemplate = true;
   }
   String maxAttemptsAttr = element.getAttribute("max-attempts");
   if (StringUtils.hasText(maxAttemptsAttr)) {
     BeanDefinitionBuilder retryPolicyBuilder =
         BeanDefinitionBuilder.genericBeanDefinition(SimpleRetryPolicy.class);
     IntegrationNamespaceUtils.setValueIfAttributeDefined(
         retryPolicyBuilder, element, "max-attempts");
     retryTemplateBuilder.addPropertyValue("retryPolicy", retryPolicyBuilder.getBeanDefinition());
     customTemplate = true;
   }
   if (customTemplate) {
     builder.addPropertyValue("retryTemplate", retryTemplateBuilder.getBeanDefinition());
   }
   String recoveryChannelAttr = element.getAttribute("recovery-channel");
   if (StringUtils.hasText(recoveryChannelAttr)) {
     BeanDefinitionBuilder emsrBuilder =
         BeanDefinitionBuilder.genericBeanDefinition(ErrorMessageSendingRecoverer.class);
     emsrBuilder.addConstructorArgReference(recoveryChannelAttr);
     IntegrationNamespaceUtils.setValueIfAttributeDefined(emsrBuilder, element, "send-timeout");
     builder.addPropertyValue("recoveryCallback", emsrBuilder.getBeanDefinition());
   }
   return builder.getBeanDefinition();
 }
  /** Utility method to configure HeaderMapper for Inbound and Outbound channel adapters/gateway */
  public static void configureHeaderMapper(
      Element element,
      BeanDefinitionBuilder rootBuilder,
      ParserContext parserContext,
      Class<?> headerMapperClass,
      String replyHeaderValue) {
    String defaultMappedReplyHeadersAttributeName = "mapped-reply-headers";
    if (!StringUtils.hasText(replyHeaderValue)) {
      replyHeaderValue = defaultMappedReplyHeadersAttributeName;
    }
    boolean hasHeaderMapper = element.hasAttribute("header-mapper");
    boolean hasMappedRequestHeaders = element.hasAttribute("mapped-request-headers");
    boolean hasMappedReplyHeaders = element.hasAttribute(replyHeaderValue);

    if (hasHeaderMapper && (hasMappedRequestHeaders || hasMappedReplyHeaders)) {
      parserContext
          .getReaderContext()
          .error(
              "The 'header-mapper' attribute is mutually exclusive with"
                  + " 'mapped-request-headers' or 'mapped-reply-headers'. "
                  + "You can only use one or the others",
              element);
    }

    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(rootBuilder, element, "header-mapper");

    if (hasMappedRequestHeaders || hasMappedReplyHeaders) {
      BeanDefinitionBuilder headerMapperBuilder =
          BeanDefinitionBuilder.genericBeanDefinition(headerMapperClass);

      if (hasMappedRequestHeaders) {
        headerMapperBuilder.addPropertyValue(
            "requestHeaderNames", element.getAttribute("mapped-request-headers"));
      }
      if (hasMappedReplyHeaders) {
        headerMapperBuilder.addPropertyValue(
            "replyHeaderNames", element.getAttribute(replyHeaderValue));
      }

      rootBuilder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition());
    }
  }
 /**
  * Override this method to control the registration process and return the bean name. If parsing a
  * bean definition whose name can be auto-generated, consider using {@link #parseConsumer(Element,
  * ParserContext)} instead.
  *
  * @param element The element.
  * @param parserContext The parser context.
  * @return The bean component definition.
  */
 protected BeanComponentDefinition doParseAndRegisterConsumer(
     Element element, ParserContext parserContext) {
   AbstractBeanDefinition definition = this.parseConsumer(element, parserContext);
   if (definition == null) {
     parserContext
         .getReaderContext()
         .error("Consumer parsing must return an AbstractBeanDefinition.", element);
   }
   String order = element.getAttribute(IntegrationNamespaceUtils.ORDER);
   if (StringUtils.hasText(order)) {
     definition.getPropertyValues().addPropertyValue(IntegrationNamespaceUtils.ORDER, order);
   }
   String beanName =
       BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry());
   String[] handlerAlias = IntegrationNamespaceUtils.generateAlias(element);
   BeanComponentDefinition beanComponentDefinition =
       new BeanComponentDefinition(definition, beanName, handlerAlias);
   parserContext.registerBeanComponent(beanComponentDefinition);
   return beanComponentDefinition;
 }
  @Override
  protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
    BeanDefinitionBuilder metadataBuilder =
        BeanDefinitionBuilder.genericBeanDefinition(PollerMetadata.class);
    if (element.hasAttribute("ref")) {
      parserContext
          .getReaderContext()
          .error(
              "the 'ref' attribute must not be present on the top-level 'poller' element", element);
    }

    configureTrigger(element, metadataBuilder, parserContext);

    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        metadataBuilder, element, "max-messages-per-poll");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        metadataBuilder, element, "receive-timeout");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
        metadataBuilder, element, "task-executor");

    Element txElement = DomUtils.getChildElementByTagName(element, "transactional");
    Element adviceChainElement = DomUtils.getChildElementByTagName(element, "advice-chain");
    IntegrationNamespaceUtils.configureAndSetAdviceChainIfPresent(
        adviceChainElement, txElement, metadataBuilder.getRawBeanDefinition(), parserContext);

    if (txElement != null) {
      IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
          metadataBuilder,
          txElement,
          "synchronization-factory",
          "transactionSynchronizationFactory");
    } else if (adviceChainElement != null) {
      IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
          metadataBuilder,
          adviceChainElement,
          "synchronization-factory",
          "transactionSynchronizationFactory");
    }

    String errorChannel = element.getAttribute("error-channel");
    if (StringUtils.hasText(errorChannel)) {
      BeanDefinitionBuilder errorHandler =
          BeanDefinitionBuilder.genericBeanDefinition(MessagePublishingErrorHandler.class);
      errorHandler.addPropertyReference("defaultErrorChannel", errorChannel);
      metadataBuilder.addPropertyValue("errorHandler", errorHandler.getBeanDefinition());
    }
    return metadataBuilder.getBeanDefinition();
  }
  @Override
  protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
    BeanDefinitionBuilder builder =
        BeanDefinitionBuilder.genericBeanDefinition(AmqpOutboundEndpoint.class);
    String amqpTemplateRef = element.getAttribute("amqp-template");
    if (!StringUtils.hasText(amqpTemplateRef)) {
      amqpTemplateRef = "amqpTemplate";
    }
    builder.addConstructorArgReference(amqpTemplateRef);
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "exchange-name", true);
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        builder, element, "exchange-name-expression");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "routing-key", true);
    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        builder, element, "routing-key-expression");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-delivery-mode");
    IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "lazy-connect");

    IntegrationNamespaceUtils.configureHeaderMapper(
        element, builder, parserContext, DefaultAmqpHeaderMapper.class, null);

    IntegrationNamespaceUtils.setValueIfAttributeDefined(
        builder, element, "confirm-correlation-expression");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
        builder, element, "confirm-ack-channel");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
        builder, element, "confirm-nack-channel");
    IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "return-channel");

    return builder.getBeanDefinition();
  }
 @Override
 protected void doParse(
     Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
   builder.addConstructorArgValue(this.expectReply);
   String inputChannelAttributeName = this.getInputChannelAttributeName();
   String inputChannelRef = element.getAttribute(inputChannelAttributeName);
   if (!StringUtils.hasText(inputChannelRef)) {
     parserContext
         .getReaderContext()
         .error("a '" + inputChannelAttributeName + "' reference is required", element);
   }
   builder.addPropertyReference("requestChannel", inputChannelRef);
   if (this.expectReply) {
     IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel");
     IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout");
     IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout");
     IntegrationNamespaceUtils.setValueIfAttributeDefined(
         builder, element, "extract-reply-payload");
     IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-key");
     IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "convert-exceptions");
   } else {
     IntegrationNamespaceUtils.setValueIfAttributeDefined(
         builder, element, "send-timeout", "requestTimeout");
   }
   IntegrationNamespaceUtils.setValueIfAttributeDefined(
       builder, element, "supported-methods", "supportedMethodNames");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-payload-type");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "view-name");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "errors-key");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "error-code");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
       builder, element, "message-converters");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "header-mapper");
 }
 @Override
 protected BeanDefinitionBuilder buildBeanDefinition(
     Element element, ParserContext parserContext) {
   BeanDefinitionBuilder builder =
       BeanDefinitionBuilder.genericBeanDefinition(AmqpChannelFactoryBean.class);
   String messageDriven = element.getAttribute("message-driven");
   if (StringUtils.hasText(messageDriven)) {
     builder.addConstructorArgValue(messageDriven);
   }
   String connectionFactory = element.getAttribute("connection-factory");
   if (!StringUtils.hasText(connectionFactory)) {
     connectionFactory = "rabbitConnectionFactory";
   }
   builder.addPropertyReference("connectionFactory", connectionFactory);
   if ("channel".equals(element.getLocalName())) {
     builder.addPropertyValue("pubSub", false);
   } else if ("publish-subscribe-channel".equals(element.getLocalName())) {
     builder.addPropertyValue("pubSub", true);
   }
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "acknowledge-mode");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "advice-chain");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "amqp-admin");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "channel-transacted");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "concurrent-consumers");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encoding");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-handler");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "exchange");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(
       builder, element, "expose-listener-channel");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-converter");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
       builder, element, "message-properties-converter");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "phase");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "prefetch-count");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "queue-name");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "receive-timeout");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "recovery-interval");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "shutdown-timeout");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "task-executor");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
       builder, element, "transaction-attribute");
   IntegrationNamespaceUtils.setReferenceIfAttributeDefined(
       builder, element, "transaction-manager");
   IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "tx-size");
   return builder;
 }
  private ManagedMap<String, Object> buildVariablesMap(
      final Element element, final ParserContext parserContext, List<Element> variableElements) {
    @SuppressWarnings("serial")
    ManagedMap<String, Object> variableMap =
        new ManagedMap<String, Object>() {

          @Override
          public Object put(String key, Object value) {
            if (this.containsKey(key)) {
              parserContext.getReaderContext().error("Duplicated variable: " + key, element);
            }
            return super.put(key, value);
          }
        };

    for (Element childElement : variableElements) {
      String variableName = childElement.getAttribute("name");
      String variableValue = childElement.getAttribute("value");
      String variableRef = childElement.getAttribute("ref");
      if (!(StringUtils.hasText(variableValue) ^ StringUtils.hasText(variableRef))) {
        parserContext
            .getReaderContext()
            .error(
                "Exactly one of the 'ref' attribute or 'value' attribute, "
                    + " is required for element "
                    + IntegrationNamespaceUtils.createElementDescription(element)
                    + ".",
                element);
      }
      if (StringUtils.hasText(variableValue)) {
        variableMap.put(variableName, variableValue);
      } else {
        variableMap.put(variableName, new RuntimeBeanReference(variableRef));
      }
    }

    String variables = element.getAttribute("variables");
    if (StringUtils.hasText(variables)) {
      String[] variablePairs = StringUtils.commaDelimitedListToStringArray(variables);
      for (String variablePair : variablePairs) {
        String[] variableValue = variablePair.split("=");
        if (variableValue.length != 2) {
          parserContext
              .getReaderContext()
              .error(
                  "Variable declarations in the 'variable' attribute must have the "
                      + "form 'var=value'; found : '"
                      + variablePair
                      + "'",
                  element);
        }
        String variable = variableValue[0].trim();
        String value = variableValue[1];
        if (variable.endsWith("-ref")) {
          variable = variable.substring(0, variable.indexOf("-ref"));
          variableMap.put(variable, new RuntimeBeanReference(value));
        } else {
          variableMap.put(variable, value);
        }
      }
    }

    return variableMap;
  }
  @Override
  @SuppressWarnings("rawtypes")
  public BeanDefinition parse(final Element element, ParserContext parserContext) {
    boolean isNested = parserContext.isNested();

    final Map<String, Object> gatewayAttributes = new HashMap<String, Object>();
    gatewayAttributes.put("name", element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE));
    gatewayAttributes.put(
        "defaultPayloadExpression", element.getAttribute("default-payload-expression"));
    gatewayAttributes.put(
        "defaultRequestChannel",
        element.getAttribute(isNested ? "request-channel" : "default-request-channel"));
    gatewayAttributes.put(
        "defaultReplyChannel",
        element.getAttribute(isNested ? "reply-channel" : "default-reply-channel"));
    gatewayAttributes.put("errorChannel", element.getAttribute("error-channel"));
    gatewayAttributes.put("asyncExecutor", element.getAttribute("async-executor"));
    gatewayAttributes.put("mapper", element.getAttribute("mapper"));
    gatewayAttributes.put(
        "defaultReplyTimeout",
        element.getAttribute(isNested ? "reply-timeout" : "default-reply-timeout"));
    gatewayAttributes.put(
        "defaultRequestTimeout",
        element.getAttribute(isNested ? "request-timeout" : "default-request-timeout"));

    List<Element> headerElements = DomUtils.getChildElementsByTagName(element, "default-header");
    if (!CollectionUtils.isEmpty(headerElements)) {
      List<Map<String, Object>> headers = new ArrayList<Map<String, Object>>(headerElements.size());
      for (Element e : headerElements) {
        Map<String, Object> header = new HashMap<String, Object>();
        header.put("name", e.getAttribute("name"));
        header.put("value", e.getAttribute("value"));
        header.put("expression", e.getAttribute("expression"));
        headers.add(header);
      }
      gatewayAttributes.put("defaultHeaders", headers.toArray(new Map[headers.size()]));
    }

    List<Element> methodElements = DomUtils.getChildElementsByTagName(element, "method");
    if (!CollectionUtils.isEmpty(methodElements)) {
      Map<String, BeanDefinition> methodMetadataMap = new ManagedMap<String, BeanDefinition>();
      for (Element methodElement : methodElements) {
        String methodName = methodElement.getAttribute("name");
        BeanDefinitionBuilder methodMetadataBuilder =
            BeanDefinitionBuilder.genericBeanDefinition(GatewayMethodMetadata.class);
        methodMetadataBuilder.addPropertyValue(
            "requestChannelName", methodElement.getAttribute("request-channel"));
        methodMetadataBuilder.addPropertyValue(
            "replyChannelName", methodElement.getAttribute("reply-channel"));
        methodMetadataBuilder.addPropertyValue(
            "requestTimeout", methodElement.getAttribute("request-timeout"));
        methodMetadataBuilder.addPropertyValue(
            "replyTimeout", methodElement.getAttribute("reply-timeout"));

        boolean hasMapper = StringUtils.hasText(element.getAttribute("mapper"));
        Assert.state(
            !hasMapper || !StringUtils.hasText(element.getAttribute("payload-expression")),
            "'payload-expression' is not allowed when a 'mapper' is provided");

        IntegrationNamespaceUtils.setValueIfAttributeDefined(
            methodMetadataBuilder, methodElement, "payload-expression");

        List<Element> invocationHeaders =
            DomUtils.getChildElementsByTagName(methodElement, "header");
        if (!CollectionUtils.isEmpty(invocationHeaders)) {
          Assert.state(!hasMapper, "header elements are not allowed when a 'mapper' is provided");

          Map<String, Object> headerExpressions = new ManagedMap<String, Object>();
          for (Element headerElement : invocationHeaders) {
            BeanDefinition expressionDef =
                IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression(
                    "value", "expression", parserContext, headerElement, true);

            headerExpressions.put(headerElement.getAttribute("name"), expressionDef);
          }
          methodMetadataBuilder.addPropertyValue("headerExpressions", headerExpressions);
        }
        methodMetadataMap.put(methodName, methodMetadataBuilder.getBeanDefinition());
      }

      gatewayAttributes.put("methods", methodMetadataMap);
    }

    gatewayAttributes.put("serviceInterface", element.getAttribute("service-interface"));

    BeanDefinitionHolder gatewayHolder = this.registrar.parse(gatewayAttributes);
    if (isNested) {
      return gatewayHolder.getBeanDefinition();
    } else {
      BeanDefinitionReaderUtils.registerBeanDefinition(gatewayHolder, parserContext.getRegistry());
      return null;
    }
  }