/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.config.spring.dsl.model.internal; import static java.util.Arrays.asList; import static java.util.Optional.empty; import static java.util.stream.Stream.of; import static org.apache.commons.lang.StringUtils.isBlank; import static org.mule.runtime.api.util.Preconditions.checkArgument; import static org.mule.runtime.config.spring.dsl.declaration.DefaultXmlArtifactDeclarationLoader.TRANSFORM_IDENTIFIER; import static org.mule.runtime.config.spring.dsl.processor.xml.XmlCustomAttributeHandler.DECLARED_PREFIX; import static org.mule.runtime.config.spring.dsl.processor.xml.XmlCustomAttributeHandler.IS_CDATA; import static org.mule.runtime.extension.api.ExtensionConstants.POOLING_PROFILE_PARAMETER_NAME; import static org.mule.runtime.extension.api.ExtensionConstants.RECONNECTION_STRATEGY_PARAMETER_NAME; import static org.mule.runtime.extension.api.ExtensionConstants.REDELIVERY_POLICY_PARAMETER_NAME; import static org.mule.runtime.extension.api.ExtensionConstants.TARGET_PARAMETER_NAME; import static org.mule.runtime.extension.api.ExtensionConstants.TLS_PARAMETER_NAME; import static org.mule.runtime.extension.api.util.XmlModelUtils.buildSchemaLocation; import static org.mule.runtime.internal.dsl.DslConstants.CONFIG_ATTRIBUTE_NAME; import static org.mule.runtime.internal.dsl.DslConstants.CORE_NAMESPACE; import static org.mule.runtime.internal.dsl.DslConstants.CORE_PREFIX; import static org.mule.runtime.internal.dsl.DslConstants.EE_NAMESPACE; import static org.mule.runtime.internal.dsl.DslConstants.EE_PREFIX; import static org.mule.runtime.internal.dsl.DslConstants.NAME_ATTRIBUTE_NAME; import static org.mule.runtime.internal.dsl.DslConstants.POOLING_PROFILE_ELEMENT_IDENTIFIER; import static org.mule.runtime.internal.dsl.DslConstants.RECONNECT_ELEMENT_IDENTIFIER; import static org.mule.runtime.internal.dsl.DslConstants.RECONNECT_FOREVER_ELEMENT_IDENTIFIER; import static org.mule.runtime.internal.dsl.DslConstants.REDELIVERY_POLICY_ELEMENT_IDENTIFIER; import static org.mule.runtime.internal.dsl.DslConstants.TLS_CONTEXT_ELEMENT_IDENTIFIER; import static org.mule.runtime.internal.dsl.DslConstants.TLS_PREFIX; import org.mule.metadata.api.model.MetadataType; import org.mule.runtime.api.component.ComponentIdentifier; import org.mule.runtime.api.meta.model.ComponentModel; import org.mule.runtime.api.meta.model.config.ConfigurationModel; import org.mule.runtime.api.meta.model.parameter.ParameterModel; import org.mule.runtime.api.util.Reference; import org.mule.runtime.config.spring.dsl.model.DslElementModel; import org.mule.runtime.config.spring.dsl.model.XmlDslElementModelConverter; import org.mule.runtime.dsl.api.component.config.ComponentConfiguration; import org.mule.runtime.extension.api.dsl.syntax.DslElementSyntax; import org.mule.runtime.extension.api.util.ExtensionModelUtils; import java.util.List; import java.util.Optional; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Default implementation of {@link XmlDslElementModelConverter} * * @since 4.0 */ public class DefaultXmlDslElementModelConverter implements XmlDslElementModelConverter { private static final List<String> INFRASTRUCTURE_NAMES = asList(CONFIG_ATTRIBUTE_NAME, NAME_ATTRIBUTE_NAME, POOLING_PROFILE_ELEMENT_IDENTIFIER, RECONNECT_ELEMENT_IDENTIFIER, RECONNECT_FOREVER_ELEMENT_IDENTIFIER, REDELIVERY_POLICY_ELEMENT_IDENTIFIER, TLS_CONTEXT_ELEMENT_IDENTIFIER, TLS_PARAMETER_NAME, POOLING_PROFILE_PARAMETER_NAME, RECONNECTION_STRATEGY_PARAMETER_NAME, REDELIVERY_POLICY_PARAMETER_NAME, TARGET_PARAMETER_NAME); private final Document doc; public DefaultXmlDslElementModelConverter(Document owner) { this.doc = owner; } /** * {@inheritDoc} */ @Override public Element asXml(DslElementModel elementModel) { Object model = elementModel.getModel(); checkArgument(model instanceof ConfigurationModel || model instanceof ComponentModel || model instanceof MetadataType, "The element must be either a MetadataType, ConfigurationModel or a ComponentModel"); DslElementSyntax dsl = elementModel.getDsl(); Element componentRoot = createElement(dsl, elementModel.getConfiguration()); if (isEETransform(componentRoot)) { return populateEETransform(elementModel); } writeApplicationElement(componentRoot, elementModel, componentRoot); return componentRoot; } private String getPrefix(DslElementSyntax dsl, ComponentConfiguration configuration) { return configuration.getProperty(DECLARED_PREFIX).isPresent() ? configuration.getProperty(DECLARED_PREFIX).get().toString() : dsl.getPrefix(); } private void writeApplicationElement(Element element, DslElementModel<?> elementModel, Element parentNode) { populateInfrastructureConfiguration(element, elementModel); if (elementModel.getContainedElements().isEmpty() && elementModel.getValue().isPresent()) { setTextContentElement(element, elementModel, parentNode); return; } elementModel.getContainedElements().stream() .filter(c -> !isInfrastructure(c)) .forEach(inner -> { DslElementSyntax innerDsl = inner.getDsl(); Reference<Boolean> configured = new Reference<>(false); if (innerDsl.supportsAttributeDeclaration() && inner.getValue().isPresent()) { getCustomizedValue(inner).ifPresent(value -> { configured.set(true); element.setAttribute(innerDsl.getAttributeName(), value); }); } if (!configured.get() && innerDsl.supportsChildDeclaration()) { Element childElement = createElement(innerDsl, inner.getConfiguration()); writeApplicationElement(childElement, inner, element); } }); if (parentNode != element) { parentNode.appendChild(element); } } private boolean isEETransform(Element parentNode) { // TODO EE-5398: Update transform namespace to ee return parentNode.getNamespaceURI().equals(CORE_NAMESPACE) && parentNode.getNodeName().equals(TRANSFORM_IDENTIFIER); } private Element createElement(DslElementSyntax dsl, Optional<ComponentConfiguration> configuration) { return configuration.isPresent() ? createElement(dsl.getElementName(), getPrefix(dsl, configuration.get()), dsl.getNamespace()) : createElement(dsl); } private void setTextContentElement(Element element, DslElementModel<?> elementModel, Element parentNode) { getCustomizedValue(elementModel).ifPresent(value -> { DslElementSyntax dsl = elementModel.getDsl(); if (dsl.supportsChildDeclaration() && !dsl.supportsAttributeDeclaration()) { if (elementModel.getConfiguration().map(c -> c.getProperty(IS_CDATA).isPresent()).orElse(false)) { element.appendChild(doc.createCDATASection(value)); } else { element.setTextContent(value); } if (parentNode != element) { parentNode.appendChild(element); } } else { parentNode.setAttribute(dsl.getAttributeName(), value); } }); } private Optional<String> getCustomizedValue(DslElementModel elementModel) { String value = (String) elementModel.getValue().get(); if (elementModel.isExplicitInDsl()) { return Optional.of(value); } Optional<String> defaultValue = getDefaultValue(elementModel.getDsl().getAttributeName(), elementModel.getModel()); if (!defaultValue.isPresent() || !defaultValue.get().equals(value)) { return Optional.of(value); } return empty(); } private Optional<String> getDefaultValue(String name, Object model) { if (model instanceof ParameterModel) { return ExtensionModelUtils.getDefaultValue((ParameterModel) model); } return ExtensionModelUtils.getDefaultValue(name, (MetadataType) model); } private Element createElement(DslElementSyntax dsl) { return createElement(dsl.getElementName(), dsl.getPrefix(), dsl.getNamespace()); } private Element createElement(String name, String prefix, String namespace) { if (!prefix.equals(CORE_PREFIX)) { addSchemaLocationIfNeeded(prefix, namespace, buildSchemaLocation(prefix, namespace)); return doc.createElementNS(namespace, prefix + ":" + name); } else { doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", CORE_NAMESPACE); return doc.createElementNS(CORE_NAMESPACE, name); } } private boolean isInfrastructure(DslElementModel elementModel) { Object model = elementModel.getModel(); if (model instanceof ParameterModel) { return ExtensionModelUtils.isInfrastructure((ParameterModel) model); } return INFRASTRUCTURE_NAMES.contains(elementModel.getDsl().getAttributeName()) || INFRASTRUCTURE_NAMES.contains(elementModel.getDsl().getElementName()); } private void populateInfrastructureConfiguration(Element element, DslElementModel<?> elementModel) { elementModel.getContainedElements().stream() .filter(this::isInfrastructure).forEach(e -> { if (e.getContainedElements().isEmpty() && e.getValue().isPresent()) { element.setAttribute(e.getDsl().getAttributeName(), (String) e.getValue().get()); } else { Optional<ComponentConfiguration> config = e.getConfiguration(); config.ifPresent(c -> { if (c.getIdentifier().getNamespace().contains(TLS_PREFIX)) { element.appendChild(createTLS(c)); } else if (c.getIdentifier().getNamespace().contains(EE_PREFIX)) { element.appendChild(createEE(c)); } else { element.appendChild(clone(c)); } }); } }); elementModel.getConfiguration() .ifPresent(c -> of(NAME_ATTRIBUTE_NAME, CONFIG_ATTRIBUTE_NAME) .forEach(name -> { String value = c.getParameters().get(name); if (!isBlank(value)) { element.setAttribute(name, value); } })); } private Element populateEETransform(DslElementModel<?> elementModel) { Element transform = doc.createElementNS(EE_NAMESPACE, EE_PREFIX + ":" + TRANSFORM_IDENTIFIER); // write set-payload and set-attributes elementModel.getContainedElements().stream() .filter(e -> !((ComponentIdentifier) e.getIdentifier().get()).getName().equals("general")).forEach(e -> { if (e.getContainedElements().isEmpty() && e.getValue().isPresent()) { transform.setAttribute(e.getDsl().getAttributeName(), (String) e.getValue().get()); } else { e.getConfiguration().ifPresent(c -> transform.appendChild(createTransformTextElement((ComponentConfiguration) c))); } }); // write set-variable elementModel.getContainedElements().stream() .filter(e -> ((ComponentIdentifier) e.getIdentifier().get()).getName().equals("general")) .forEach(e -> e.getContainedElements().stream().findFirst() .ifPresent(setVariablesElement -> ((DslElementModel) setVariablesElement).getContainedElements().stream() .forEach(setVariable -> ((DslElementModel) setVariable).getConfiguration() .ifPresent(c -> transform.appendChild(createTransformTextElement((ComponentConfiguration) c)))))); return transform; } private Element createTLS(ComponentConfiguration config) { String namespaceURI = "http://www.mulesoft.org/schema/mule/tls"; String tlsSchemaLocation = "http://www.mulesoft.org/schema/mule/tls/current/mule-tls.xsd"; addSchemaLocationIfNeeded(TLS_PREFIX, namespaceURI, tlsSchemaLocation); Element nested = doc.createElementNS(namespaceURI, TLS_PREFIX + ":" + config.getIdentifier().getName()); config.getParameters().forEach(nested::setAttribute); config.getNestedComponents().forEach(inner -> nested.appendChild(createTLS(inner))); return nested; } private Element createEE(ComponentConfiguration config) { String namespaceURI = EE_NAMESPACE; String eeSchemaLocation = buildSchemaLocation(EE_PREFIX, EE_NAMESPACE); addSchemaLocationIfNeeded(EE_PREFIX, namespaceURI, eeSchemaLocation); Element nested = doc.createElementNS(namespaceURI, EE_PREFIX + ":" + config.getIdentifier().getName()); config.getParameters().forEach(nested::setAttribute); config.getNestedComponents().forEach(inner -> nested.appendChild(clone(inner))); return nested; } private Element createTransformTextElement(ComponentConfiguration config) { String namespaceURI = EE_NAMESPACE; String eeSchemaLocation = buildSchemaLocation(EE_PREFIX, EE_NAMESPACE); addSchemaLocationIfNeeded(EE_PREFIX, namespaceURI, eeSchemaLocation); Element nested = doc.createElementNS(namespaceURI, EE_PREFIX + ":" + config.getIdentifier().getName()); config.getParameters().forEach(nested::setAttribute); // TODO: EE-5393: Add CDATA section config.getNestedComponents().stream() .filter(inner -> inner.getValue().isPresent()) .forEach(inner -> nested.appendChild(doc.createCDATASection(inner.getValue().get()))); return nested; } private void addSchemaLocationIfNeeded(String prefix, String namespaceURI, String schemaLocation) { Attr schemaLocationAttribute = doc.getDocumentElement().getAttributeNode("xsi:schemaLocation"); if (schemaLocationAttribute != null && !schemaLocationAttribute.getValue().contains(namespaceURI)) { doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + prefix, namespaceURI); doc.getDocumentElement().setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation", schemaLocationAttribute.getValue() + " " + namespaceURI + " " + schemaLocation); } } private Element clone(ComponentConfiguration config) { Element element = doc.createElement(config.getIdentifier().getName()); config.getParameters().forEach(element::setAttribute); config.getNestedComponents().forEach(nested -> element.appendChild(clone(nested))); return element; } }