All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mule.config.spring.SpringXmlConfigurationMuleArtifactFactory Maven / Gradle / Ivy

There is a newer version: 3.9.0
Show newest version
/*
 * 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.config.spring;

import org.mule.api.MuleContext;
import org.mule.api.config.ConfigurationException;
import org.mule.api.construct.Pipeline;
import org.mule.api.context.MuleContextFactory;
import org.mule.common.MuleArtifact;
import org.mule.common.MuleArtifactFactoryException;
import org.mule.common.config.XmlConfigurationCallback;
import org.mule.common.config.XmlConfigurationMuleArtifactFactory;
import org.mule.config.ConfigResource;
import org.mule.context.DefaultMuleContextFactory;
import org.mule.util.IOUtils;
import org.mule.util.StringUtils;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.DOMReader;
import org.w3c.dom.Node;

public class SpringXmlConfigurationMuleArtifactFactory implements XmlConfigurationMuleArtifactFactory
{

    public static final String BEANS_ELEMENT = "beans";

    public static final String REFS_SUFFIX = "-refs";
    public static final String REFS_TOKENS = " \t"; // Space, Tab
    public static final String REF_SUFFIX = "-ref";
    public static final String REF_ATTRIBUTE_NAME = "ref";
    public static final String NAME_ATTRIBUTE_NAME = "name";

    private Map builders = new HashMap();
    private Map contexts = new HashMap();

    @Override
    public MuleArtifact getArtifact(org.w3c.dom.Element element, XmlConfigurationCallback callback)
            throws MuleArtifactFactoryException
    {
        return doGetArtifact(element, callback, false);
    }

    @Override
    public MuleArtifact getArtifactForMessageProcessor(org.w3c.dom.Element element, XmlConfigurationCallback callback)
            throws MuleArtifactFactoryException
    {
        return doGetArtifact(element, callback, true);
    }

    protected String getArtifactMuleConfig(String flowName, org.w3c.dom.Element element, final XmlConfigurationCallback callback, boolean embedInFlow) throws MuleArtifactFactoryException
    {
        Map schemaLocations = new HashMap();
        schemaLocations.put("http://www.mulesoft.org/schema/mule/core", "http://www.mulesoft.org/schema/mule/core/current/mule.xsd");

        Document document = DocumentHelper.createDocument();

        // the rootElement is the root of the document
        Element rootElement = document.addElement("mule", "http://www.mulesoft.org/schema/mule/core");

        org.w3c.dom.Element[] placeholders = callback.getPropertyPlaceholders();

        for (org.w3c.dom.Element placeholder : placeholders)
        {
            try
            {
                Element newPlaceholder = convert(placeholder);
                rootElement.add(newPlaceholder);
                addSchemaLocation(placeholder, callback, schemaLocations);
            }
            catch (ParserConfigurationException e)
            {
                throw new MuleArtifactFactoryException("Error parsing XML", e);
            }

        }

        // the parentElement is the parent of the element we are adding
        Element parentElement = rootElement;
        addSchemaLocation(element, callback, schemaLocations);
        if (embedInFlow)
        {
            // Need to put the message processor in a valid flow. Our default flow is:
            //            ""
            //          + ""
            parentElement = rootElement.addElement("flow", "http://www.mulesoft.org/schema/mule/core");
            parentElement.addAttribute("name", flowName);
        }
        try
        {
            parentElement.add(convert(element));

            processGlobalReferences(element, callback, rootElement, schemaLocations);

            // For message sources to work, the flow should be valid, this means needs to have a MP
            if (embedInFlow)
            {
                parentElement.addElement("logger", "http://www.mulesoft.org/schema/mule/core");
            }

            setSchemaLocation(rootElement, schemaLocations);

            return document.asXML();
        }
        catch (Throwable t)
        {
            throw new MuleArtifactFactoryException("Error generating minimal XML configuration.", t);
        }

    }

    private void processGlobalReferences(org.w3c.dom.Element element, XmlConfigurationCallback callback, Element rootElement, Map schemaLocations) throws ParserConfigurationException
    {
        processGlobalReferencesInAttributes(element, callback, rootElement, schemaLocations);

        processGlobalReferencesInChildElements(element, callback, rootElement, schemaLocations);
    }

    private void processGlobalReferencesInChildElements(org.w3c.dom.Element element, XmlConfigurationCallback callback, Element rootElement, Map schemaLocations) throws ParserConfigurationException
    {
        if (element != null && element.getChildNodes() != null)
        {
            // Look for references in first level of child nodes
            for (int i = 0; i < element.getChildNodes().getLength(); i++)
            {
                processGlobalReferencesInAttributes(element.getChildNodes().item(i), callback, rootElement, schemaLocations);
            }
        }
    }

    private void processGlobalReferencesInAttributes(Node element, XmlConfigurationCallback callback, Element rootElement, Map schemaLocations) throws ParserConfigurationException
    {
        if (element != null && element.getAttributes() != null)
        {
            for (int i = 0; i < element.getAttributes().getLength(); i++)
            {
                String attributeName = element.getAttributes().item(i).getLocalName();
                if (attributeName != null && ((attributeName.endsWith(REF_SUFFIX) || attributeName.equals(REF_ATTRIBUTE_NAME)) || (element.getNodeName().endsWith(REF_SUFFIX) && attributeName.equals(NAME_ATTRIBUTE_NAME))))
                {
                    org.w3c.dom.Element dependentElement = callback.getGlobalElement(element.getAttributes()
                                                                                             .item(i)
                                                                                             .getNodeValue());
                    addReferencedGlobalElement(callback, rootElement, dependentElement, schemaLocations);
                }
                else if (attributeName != null && attributeName.endsWith(REFS_SUFFIX))
                {
                    StringTokenizer refs = new StringTokenizer(element.getAttributes().item(i).getNodeValue(), REFS_TOKENS);

                    while(refs.hasMoreTokens())
                    {
                        String referenceName = refs.nextToken();
                        if (StringUtils.isNotBlank(referenceName))
                        {
                            org.w3c.dom.Element dependentElement = callback.getGlobalElement(referenceName);
                            addReferencedGlobalElement(callback, rootElement, dependentElement, schemaLocations);
                        }

                    }
                }

            }
        }

    }

    private void addReferencedGlobalElement(XmlConfigurationCallback callback, Element rootElement, org.w3c.dom.Element dependentElement, Map schemaLocations) throws ParserConfigurationException
    {
        if (dependentElement != null)
        {
            if (isSpringBean(dependentElement))
            {
                wrapElementInSpringBeanContainer(rootElement, dependentElement);
            }
            else
            {
                rootElement.add(convert(dependentElement));
                addSchemaLocation(dependentElement, callback, schemaLocations);
            }
            processGlobalReferences(dependentElement, callback, rootElement, schemaLocations);
        }
    }

    private void wrapElementInSpringBeanContainer(Element rootElement, org.w3c.dom.Element dependentElement) throws ParserConfigurationException
    {
        String namespaceUri = dependentElement.getNamespaceURI();
        Namespace namespace = new Namespace(dependentElement.getPrefix(), namespaceUri);
        Element beans = rootElement.element(new QName(BEANS_ELEMENT, namespace));
        if (beans == null)
        {
            beans = rootElement.addElement(BEANS_ELEMENT, namespaceUri);
        }
        beans.add(convert(dependentElement));
    }

    private boolean isSpringBean(org.w3c.dom.Element dependentElement)
    {
        return "http://www.springframework.org/schema/beans".equals(dependentElement.getNamespaceURI());
    }

    protected MuleArtifact doGetArtifact(org.w3c.dom.Element element, final XmlConfigurationCallback callback, boolean embedInFlow)
            throws MuleArtifactFactoryException
    {
        String flowName = "flow-" + Integer.toString(element.hashCode());
        InputStream xmlConfig = IOUtils.toInputStream(getArtifactMuleConfig(flowName, element, callback, embedInFlow));

        MuleContext muleContext = null;
        SpringXmlConfigurationBuilder builder = null;
        Map environmentProperties = callback.getEnvironmentProperties();
        Properties systemProperties = System.getProperties();
        Map originalSystemProperties = new HashMap(systemProperties);

        try
        {
            ConfigResource config = new ConfigResource("embedded-datasense.xml", xmlConfig);
            // This configuration overrides the default-mule-config one to replace beans that are not required
            ConfigResource defaultConfigOverride = new ConfigResource(getClass().getClassLoader().getResource("default-mule-config-override.xml").toURI().toURL());

            if (environmentProperties != null)
            {
                systemProperties.putAll(environmentProperties);
            }
            MuleContextFactory factory = new DefaultMuleContextFactory();

            builder = new SpringXmlConfigurationBuilder(new ConfigResource[] {config, defaultConfigOverride});
            muleContext = factory.createMuleContext(builder);
            muleContext.start();

            MuleArtifact artifact = null;
            if (embedInFlow)
            {
                Pipeline pipeline = muleContext.getRegistry().lookupObject(flowName);
                if (pipeline.getMessageSource() == null)
                {
                    if (pipeline.getMessageProcessors() != null && pipeline.getMessageProcessors().size() > 0)
                    {
                        artifact = new DefaultMuleArtifact(pipeline.getMessageProcessors().get(0));
                    }
                    else
                    {
                        throw new IllegalArgumentException("artifact is null");
                    }
                }
                else
                {
                    artifact = new DefaultMuleArtifact(pipeline.getMessageSource());
                }
            }
            else
            {
                artifact = new DefaultMuleArtifact(muleContext.getRegistry().lookupObject(element.getAttribute("name")));
            }

            builders.put(artifact, builder);
            contexts.put(artifact, muleContext);
            return artifact;
        }
        catch (ConfigurationException ce)
        {
            dispose(builder, muleContext);
            throw new MuleArtifactFactoryException("There seems to be a problem in your XML configuration. Please fix and retry.", ce);
        }
        catch (Throwable t)
        {
            dispose(builder, muleContext);
            throw new MuleArtifactFactoryException("Error starting minimal XML configuration.", t);
        }
        finally
        {
            systemProperties.clear();
            systemProperties.putAll(originalSystemProperties);
            IOUtils.closeQuietly(xmlConfig);
        }
    }

    protected void addSchemaLocation(org.w3c.dom.Element element, XmlConfigurationCallback callback,
                                     Map schemaLocations)
    {
        String key = element.getNamespaceURI();
        if (key != null && !schemaLocations.containsKey(key))
        {
            schemaLocations.put(element.getNamespaceURI(), callback.getSchemaLocation(element.getNamespaceURI()));
        }
    }

    protected void setSchemaLocation(Element rootElement, Map schemaLocations)
    {
        StringBuilder schemaLocation = new StringBuilder();

        for (String key : schemaLocations.keySet())
        {
            schemaLocation.append(key + " " + schemaLocations.get(key) + "\n");
        }

        rootElement.addAttribute(
                org.dom4j.QName.get("schemaLocation", "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
                schemaLocation.toString().trim());
    }

    /**
     * Convert w3c element to dom4j element
     *
     * @throws ParserConfigurationException
     */
    public org.dom4j.Element convert(org.w3c.dom.Element element) throws ParserConfigurationException
    {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        org.w3c.dom.Document doc1 = builder.newDocument();
        org.w3c.dom.Element importedElement = (org.w3c.dom.Element) doc1.importNode(element, Boolean.TRUE);
        doc1.appendChild(importedElement);

        // Convert w3c document to dom4j document
        DOMReader reader = new DOMReader();
        org.dom4j.Document doc2 = reader.read(doc1);

        return doc2.getRootElement();
    }

    @Override
    public void returnArtifact(MuleArtifact artifact)
    {
        SpringXmlConfigurationBuilder builder = builders.remove(artifact);
        MuleContext context = contexts.remove(artifact);
        dispose(builder, context);
    }

    protected void dispose(SpringXmlConfigurationBuilder builder, MuleContext context)
    {
        try
        {
            if (context != null)
            {
                context.dispose();
            }
        }
        finally
        {
            deleteLoggingThreads();
        }
    }

    private void deleteLoggingThreads()
    {
        String[] threadsToDelete = {"Mule.log.clogging.ref.handler", "Mule.log.slf4j.ref.handler"};

        Set threadSet = Thread.getAllStackTraces().keySet();
        Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
        for (String threadToDelete : threadsToDelete)
        {
            for (Thread t : threadArray)
            {
                if (threadToDelete.equals(t.getName()))
                {
                    try
                    {
                        t.interrupt();
                    }
                    catch (SecurityException e)
                    {
                        // ignore
                    }
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy