/**
   * Validate namespaces in message. The method compares namespace declarations in the root element
   * of the received message to expected namespaces. Prefixes are important too, so differing
   * namespace prefixes will fail the validation.
   *
   * @param expectedNamespaces
   * @param receivedMessage
   */
  protected void validateNamespaces(
      Map<String, String> expectedNamespaces, Message receivedMessage) {
    if (CollectionUtils.isEmpty(expectedNamespaces)) {
      return;
    }

    if (receivedMessage.getPayload() == null
        || !StringUtils.hasText(receivedMessage.getPayload(String.class))) {
      throw new ValidationException(
          "Unable to validate message namespaces - receive message payload was empty");
    }

    log.info("Start XML namespace validation");

    Document received = XMLUtils.parseMessagePayload(receivedMessage.getPayload(String.class));

    Map<String, String> foundNamespaces =
        XMLUtils.lookupNamespaces(receivedMessage.getPayload(String.class));

    if (foundNamespaces.size() != expectedNamespaces.size()) {
      throw new ValidationException(
          "Number of namespace declarations not equal for node "
              + XMLUtils.getNodesPathName(received.getFirstChild())
              + " found "
              + foundNamespaces.size()
              + " expected "
              + expectedNamespaces.size());
    }

    for (Entry<String, String> entry : expectedNamespaces.entrySet()) {
      String namespace = entry.getKey();
      String url = entry.getValue();

      if (foundNamespaces.containsKey(namespace)) {
        if (!foundNamespaces.get(namespace).equals(url)) {
          throw new ValidationException(
              "Namespace '"
                  + namespace
                  + "' values not equal: found '"
                  + foundNamespaces.get(namespace)
                  + "' expected '"
                  + url
                  + "' in reference node "
                  + XMLUtils.getNodesPathName(received.getFirstChild()));
        } else {
          log.info(
              "Validating namespace " + namespace + " value as expected " + url + " - value OK");
        }
      } else {
        throw new ValidationException(
            "Missing namespace "
                + namespace
                + "("
                + url
                + ") in node "
                + XMLUtils.getNodesPathName(received.getFirstChild()));
      }
    }

    log.info("XML namespace validation finished successfully: All values OK");
  }
  /**
   * Validate message with a XML schema.
   *
   * @param receivedMessage
   * @param validationContext
   */
  protected void validateXMLSchema(
      Message receivedMessage, XmlMessageValidationContext validationContext) {
    if (receivedMessage.getPayload() == null
        || !StringUtils.hasText(receivedMessage.getPayload(String.class))) {
      return;
    }

    try {
      Document doc = XMLUtils.parseMessagePayload(receivedMessage.getPayload(String.class));

      if (!StringUtils.hasText(doc.getFirstChild().getNamespaceURI())) {
        return;
      }

      log.info("Starting XML schema validation ...");

      XmlValidator validator = null;
      XsdSchemaRepository schemaRepository = null;
      if (validationContext.getSchema() != null) {
        validator =
            applicationContext
                .getBean(validationContext.getSchema(), XsdSchema.class)
                .createValidator();
      } else if (validationContext.getSchemaRepository() != null) {
        schemaRepository =
            applicationContext.getBean(
                validationContext.getSchemaRepository(), XsdSchemaRepository.class);
      } else if (schemaRepositories.size() == 1) {
        schemaRepository = schemaRepositories.get(0);
      } else if (schemaRepositories.size() > 0) {
        for (XsdSchemaRepository repository : schemaRepositories) {
          if (repository.canValidate(doc)) {
            schemaRepository = repository;
          }
        }

        if (schemaRepository == null) {
          throw new CitrusRuntimeException(
              String.format(
                  "Failed to find proper schema repository in Spring bean context for validating element '%s(%s)'",
                  doc.getFirstChild().getLocalName(), doc.getFirstChild().getNamespaceURI()));
        }
      } else {
        log.warn(
            "Neither schema instance nor schema repository defined - skipping XML schema validation");
        return;
      }

      if (schemaRepository != null) {
        if (!schemaRepository.canValidate(doc)) {
          throw new CitrusRuntimeException(
              String.format(
                  "Unable to find proper XML schema definition for element '%s(%s)' in schema repository '%s'",
                  doc.getFirstChild().getLocalName(),
                  doc.getFirstChild().getNamespaceURI(),
                  schemaRepository.getName()));
        }

        List<Resource> schemas = new ArrayList<>();
        for (XsdSchema xsdSchema : schemaRepository.getSchemas()) {
          if (xsdSchema instanceof XsdSchemaCollection) {
            for (Resource resource : ((XsdSchemaCollection) xsdSchema).getSchemaResources()) {
              schemas.add(resource);
            }
          } else if (xsdSchema instanceof WsdlXsdSchema) {
            for (Resource resource : ((WsdlXsdSchema) xsdSchema).getSchemaResources()) {
              schemas.add(resource);
            }
          } else {
            synchronized (transformerFactory) {
              ByteArrayOutputStream bos = new ByteArrayOutputStream();
              try {
                transformerFactory
                    .newTransformer()
                    .transform(xsdSchema.getSource(), new StreamResult(bos));
              } catch (TransformerException e) {
                throw new CitrusRuntimeException(
                    "Failed to read schema " + xsdSchema.getTargetNamespace(), e);
              }
              schemas.add(new ByteArrayResource(bos.toByteArray()));
            }
          }
        }

        validator =
            XmlValidatorFactory.createValidator(
                schemas.toArray(new Resource[schemas.size()]), WsdlXsdSchema.W3C_XML_SCHEMA_NS_URI);
      }

      SAXParseException[] results = validator.validate(new DOMSource(doc));
      if (results.length == 0) {
        log.info("Schema of received XML validated OK");
      } else {
        log.error(
            "Schema validation failed for message:\n"
                + XMLUtils.prettyPrint(receivedMessage.getPayload(String.class)));

        // Report all parsing errors
        log.debug("Found " + results.length + " schema validation errors");
        StringBuilder errors = new StringBuilder();
        for (SAXParseException e : results) {
          errors.append(e.toString());
          errors.append("\n");
        }
        log.debug(errors.toString());

        throw new ValidationException("Schema validation failed:", results[0]);
      }
    } catch (IOException e) {
      throw new CitrusRuntimeException(e);
    } catch (SAXException e) {
      throw new CitrusRuntimeException(e);
    }
  }