/* * 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.core.config; import static java.lang.String.format; import static org.mule.runtime.core.util.StandaloneServerUtils.getMuleBase; import static org.mule.runtime.core.util.StandaloneServerUtils.getMuleHome; import org.mule.runtime.api.lifecycle.Initialisable; import org.mule.runtime.api.lifecycle.Startable; import org.mule.runtime.core.api.MuleContext; import org.mule.runtime.core.api.config.ConfigurationExtension; import org.mule.runtime.core.api.config.MuleConfiguration; import org.mule.runtime.core.api.config.MuleProperties; import org.mule.runtime.core.api.context.MuleContextAware; import org.mule.runtime.core.api.lifecycle.FatalException; import org.mule.runtime.core.api.processor.strategy.ProcessingStrategy; import org.mule.runtime.core.api.processor.strategy.ProcessingStrategyFactory; import org.mule.runtime.core.api.serialization.ObjectSerializer; import org.mule.runtime.core.config.i18n.CoreMessages; import org.mule.runtime.core.util.FileUtils; import org.mule.runtime.core.util.NetworkUtils; import org.mule.runtime.core.util.StringUtils; import org.mule.runtime.core.util.UUID; import org.mule.runtime.core.util.xmlsecurity.XMLSecureFactories; import java.io.File; import java.io.IOException; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.BooleanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Configuration info. which can be set when creating the MuleContext but becomes immutable after starting the MuleContext. */ public class DefaultMuleConfiguration implements MuleConfiguration, MuleContextAware { /** * When true, each event will keep trace information of the flows and components it traverses to be shown as part of an * exception message if an exception occurs. Switching on DEBUG level logging with automatically set this flag to true. */ public static boolean flowTrace = false; private boolean synchronous = false; /** * The type of model used for the internal system model where system created services are registered */ private String systemModelType = "seda"; private String encoding = "UTF-8"; /** * When running sychronously, return events can be received over transports that support ack or replyTo This property determines * how long to wait for a receive */ private int responseTimeout = 10000; /** * The default transaction timeout value used if no specific transaction time out has been set on the transaction config */ private int defaultTransactionTimeout = 30000; /** * The default queue timeout value used when polling queues. */ private int defaultQueueTimeout = 200; /** * The default graceful shutdown timeout used when shutting stopping mule cleanly without message loss. */ private long shutdownTimeout = 5000; /** * Where Mule stores any runtime files to disk. Note that in container mode each app will have its working dir set one level * under this dir (with app's name) in the {@link #setMuleContext} callback. * */ private String workingDirectory = "./.mule"; /** * Whether the server instance is running in client mode, which means that some services will not be started */ private boolean clientMode = false; /** the unique id for this Mule instance */ private String id; /** If this node is part of a cluster then this is the shared cluster Id */ private String clusterId = ""; /** The domain name that this instance belongs to. */ private String domainId; // Debug options private boolean cacheMessageAsBytes = true; private boolean enableStreaming = true; private boolean autoWrapMessageAwareTransform = true; /** * Whether transports and processors timeouts should be disabled in order to allow step debugging of mule flows. */ private boolean disableTimeouts = false; protected static transient Logger logger = LoggerFactory.getLogger(DefaultMuleConfiguration.class); private MuleContext muleContext; private boolean containerMode; /** * By default the Mule Expression parser will perform basic syntax checking on expressions in order to provide early feedback if * an expression is malformed. Part of the check is checking that all open braces are closed at some point. For some expressions * such as groovy, there could be a scenario where a brace is deliberately used without being closed; this would cause the * validation to fail. Users can turn off validation using this flag. */ private boolean validateExpressions = true; /** * Generic string/string map of properties in addition to standard Mule props. Used as an extension point e.g. in MMC. */ private Map<String, String> extendedProperties = new HashMap<>(); /** * Global exception strategy name to be used as default exception strategy for flows and services */ private String defaultExceptionStrategyName; /** * List of extensions defined in the configuration element at the application. */ private List<ConfigurationExtension> extensions = new ArrayList<>(); /** * The instance of {@link ObjectSerializer} to use by default * * @since 3.7.0 */ private ObjectSerializer defaultObjectSerializer; /** * The {@link ProcessingStrategyFactory factory} of the default {@link ProcessingStrategy} to be used by all * {@link org.mule.runtime.core.api.construct.Flow}s which doesn't specify otherwise * * @since 3.7.0 */ private ProcessingStrategyFactory defaultProcessingStrategyFactory; /** * Maximum size (approximately) of the transaction log files. This applies to each set of files for local transactions and xa * transactions when using queues. * * @since 3.9.0 */ private int maxQueueTransactionFilesSizeInMegabytes = 500; public DefaultMuleConfiguration() { this(false); } public DefaultMuleConfiguration(boolean containerMode) { this.containerMode = containerMode; // Apply any settings which come from the JVM system properties. applySystemProperties(); if (id == null) { id = UUID.getUUID(); } if (domainId == null) { try { domainId = NetworkUtils.getLocalHost().getHostName(); } catch (UnknownHostException e) { logger.warn("Unable to obtain hostname", e); domainId = "org.mule.runtime.core"; } } try { validateEncoding(); validateXML(); } catch (FatalException e) { throw new RuntimeException(e); } } @Override public void setMuleContext(MuleContext context) { this.muleContext = context; if (containerMode) { final String muleBase = getMuleBase().map(File::getAbsolutePath).orElse(null); // in container mode the id is the app name, have each app isolate its work dir if (!isStandalone()) { // fallback to current dir as a parent this.workingDirectory = String.format("%s/%s", getWorkingDirectory(), getId()); } else { this.workingDirectory = String.format("%s/%s/%s", muleBase.trim(), getWorkingDirectory(), getId()); } } else if (isStandalone()) { this.workingDirectory = String.format("%s/%s", getWorkingDirectory(), getId()); } } /** * Apply any settings which come from the JVM system properties. */ protected void applySystemProperties() { String p; p = System.getProperty(MuleProperties.MULE_ENCODING_SYSTEM_PROPERTY); if (p != null) { encoding = p; } else { System.setProperty(MuleProperties.MULE_ENCODING_SYSTEM_PROPERTY, encoding); } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "endpoints.synchronous"); if (p != null) { synchronous = BooleanUtils.toBoolean(p); } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "systemModelType"); if (p != null) { systemModelType = p; } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "workingDirectory"); if (p != null) { workingDirectory = p; } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "clientMode"); if (p != null) { clientMode = BooleanUtils.toBoolean(p); } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "serverId"); if (p != null) { id = p; } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "domainId"); if (p != null) { domainId = p; } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "message.cacheBytes"); if (p != null) { cacheMessageAsBytes = BooleanUtils.toBoolean(p); } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "streaming.enable"); if (p != null) { enableStreaming = BooleanUtils.toBoolean(p); } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "transform.autoWrap"); if (p != null) { autoWrapMessageAwareTransform = BooleanUtils.toBoolean(p); } p = System.getProperty(MuleProperties.MULE_FLOW_TRACE); if (p != null) { flowTrace = BooleanUtils.toBoolean(p); } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "validate.expressions"); if (p != null) { validateExpressions = Boolean.valueOf(p); } p = System.getProperty(MuleProperties.SYSTEM_PROPERTY_PREFIX + "timeout.disable"); if (p != null) { disableTimeouts = Boolean.valueOf(p); } } /** * @return {@code true} if the log is set to debug or if the system property {@code mule.flowTrace} is set to {@code true}. * {@code false} otherwise. */ public static boolean isFlowTrace() { return flowTrace || logger.isDebugEnabled(); } protected void validateEncoding() throws FatalException { // Check we have a valid and supported encoding if (!Charset.isSupported(encoding)) { throw new FatalException(CoreMessages.propertyHasInvalidValue("encoding", encoding), this); } } /** * Mule needs a proper JAXP implementation and will complain when run with a plain JDK 1.4. Use the supplied launcher or specify * a proper JAXP implementation via <code>-Djava.endorsed.dirs</code>. See the following URLs for more information: * <ul> * <li><a href="http://xerces.apache.org/xerces2-j/faq-general.html#faq-4">Xerces</a> * <li><a href="http://xml.apache.org/xalan-j/faq.html#faq-N100D6">Xalan</a> * <li><a href="http://java.sun.com/j2se/1.4.2/docs/guide/standards/">Endorsed Standards Override Mechanism</a> * </ul> */ protected void validateXML() throws FatalException { SAXParserFactory f = XMLSecureFactories.createDefault().getSAXParserFactory(); if (f == null || f.getClass().getName().indexOf("crimson") != -1) { throw new FatalException(CoreMessages.valueIsInvalidFor(f.getClass().getName(), "javax.xml.parsers.SAXParserFactory"), this); } } public void setDefaultSynchronousEndpoints(boolean synchronous) { if (verifyContextNotStarted()) { this.synchronous = synchronous; } } @Override public int getDefaultResponseTimeout() { return responseTimeout; } public void setDefaultResponseTimeout(int responseTimeout) { if (verifyContextNotStarted()) { this.responseTimeout = responseTimeout; } } @Override public String getWorkingDirectory() { return workingDirectory; } @Override public String getMuleHomeDirectory() { return getMuleHome().map(File::getAbsolutePath).orElse(null); } public void setWorkingDirectory(String workingDirectory) { if (verifyContextNotInitialized()) { try { File canonicalFile = FileUtils.openDirectory(workingDirectory); this.workingDirectory = canonicalFile.getCanonicalPath(); } catch (IOException e) { throw new IllegalArgumentException(CoreMessages.initialisationFailure("Invalid working directory").getMessage(), e); } } } @Override public int getDefaultTransactionTimeout() { return defaultTransactionTimeout; } public void setDefaultTransactionTimeout(int defaultTransactionTimeout) { if (verifyContextNotStarted()) { this.defaultTransactionTimeout = defaultTransactionTimeout; } } @Override public boolean isValidateExpressions() { return validateExpressions; } @Override public boolean isClientMode() { return clientMode; } @Override public String getDefaultEncoding() { return encoding; } public void setDefaultEncoding(String encoding) { if (verifyContextNotInitialized()) { this.encoding = encoding; } } @Override public String getId() { return id; } public void setId(String id) { if (verifyContextNotInitialized()) { if (StringUtils.isBlank(id)) { throw new IllegalArgumentException("Cannot set server id to null/blank"); } this.id = id; } } public void setClusterId(String clusterId) { this.clusterId = clusterId; } @Override public String getDomainId() { return domainId; } public void setDomainId(String domainId) { if (verifyContextNotInitialized()) { this.domainId = domainId; } } @Override public String getSystemModelType() { return systemModelType; } public void setSystemModelType(String systemModelType) { if (verifyContextNotStarted()) { this.systemModelType = systemModelType; } } public void setClientMode(boolean clientMode) { if (verifyContextNotStarted()) { this.clientMode = clientMode; } } @Override public String getSystemName() { return domainId + "." + clusterId + "." + id; } @Override public boolean isAutoWrapMessageAwareTransform() { return autoWrapMessageAwareTransform; } public void setAutoWrapMessageAwareTransform(boolean autoWrapMessageAwareTransform) { if (verifyContextNotStarted()) { this.autoWrapMessageAwareTransform = autoWrapMessageAwareTransform; } } @Override public boolean isCacheMessageAsBytes() { return cacheMessageAsBytes; } public void setCacheMessageAsBytes(boolean cacheMessageAsBytes) { if (verifyContextNotStarted()) { this.cacheMessageAsBytes = cacheMessageAsBytes; } } @Override public boolean isEnableStreaming() { return enableStreaming; } public void setEnableStreaming(boolean enableStreaming) { if (verifyContextNotStarted()) { this.enableStreaming = enableStreaming; } } protected boolean verifyContextNotInitialized() { if (muleContext != null && muleContext.getLifecycleManager().isPhaseComplete(Initialisable.PHASE_NAME)) { logger.warn("Cannot modify MuleConfiguration once the MuleContext has been initialized. Modification will be ignored."); return false; } else { return true; } } protected boolean verifyContextNotStarted() { if (muleContext != null && muleContext.getLifecycleManager().isPhaseComplete(Startable.PHASE_NAME)) { logger.warn("Cannot modify MuleConfiguration once the MuleContext has been started. Modification will be ignored."); return false; } else { return true; } } @Override public int getDefaultQueueTimeout() { return defaultQueueTimeout; } public void setDefaultQueueTimeout(int defaultQueueTimeout) { if (verifyContextNotStarted()) { this.defaultQueueTimeout = defaultQueueTimeout; } } @Override public long getShutdownTimeout() { return shutdownTimeout; } @Override public int getMaxQueueTransactionFilesSizeInMegabytes() { return maxQueueTransactionFilesSizeInMegabytes; } public void setShutdownTimeout(long shutdownTimeout) { if (verifyContextNotStarted()) { if (shutdownTimeout < 0) { throw new IllegalArgumentException(format("'shutdownTimeout' must be a possitive long. %d passed", shutdownTimeout)); } this.shutdownTimeout = shutdownTimeout; } } @Override public boolean isContainerMode() { return this.containerMode; } /** * The setting is only editable before the context has been initialized, change requests ignored afterwards. */ public void setContainerMode(boolean containerMode) { if (verifyContextNotInitialized()) { this.containerMode = containerMode; } } @Override public boolean isStandalone() { // this is our best guess return getMuleHomeDirectory() != null; } public Map<String, String> getExtendedProperties() { return extendedProperties; } public void setExtendedProperties(Map<String, String> extendedProperties) { this.extendedProperties = extendedProperties; } public void setExtendedProperty(String name, String value) { this.extendedProperties.put(name, value); } public String getExtendedProperty(String name) { return this.extendedProperties.get(name); } @Override public String getDefaultErrorHandlerName() { return defaultExceptionStrategyName; } public void setDefaultErrorHandlerName(String defaultExceptionStrategyName) { this.defaultExceptionStrategyName = defaultExceptionStrategyName; } public void setMaxQueueTransactionFilesSize(int maxQueueTransactionFilesSizeInMegabytes) { this.maxQueueTransactionFilesSizeInMegabytes = maxQueueTransactionFilesSizeInMegabytes; } @Override public boolean isDisableTimeouts() { return disableTimeouts; } /** * {@inheritDoc} */ @Override public ObjectSerializer getDefaultObjectSerializer() { return defaultObjectSerializer; } /** * {@inheritDoc} */ @Override public ProcessingStrategyFactory getDefaultProcessingStrategyFactory() { return defaultProcessingStrategyFactory; } public void setDefaultProcessingStrategyFactory(ProcessingStrategyFactory defaultProcessingStrategy) { this.defaultProcessingStrategyFactory = defaultProcessingStrategy; } public void setDefaultObjectSerializer(ObjectSerializer defaultObjectSerializer) { this.defaultObjectSerializer = defaultObjectSerializer; } public void addExtensions(List<ConfigurationExtension> extensions) { this.extensions.addAll(extensions); } @Override public <T> T getExtension(final Class<T> extensionType) { return (T) CollectionUtils.find(extensions, object -> extensionType.isAssignableFrom(object.getClass())); } public List<ConfigurationExtension> getExtensions() { if (extensions == null) { return Collections.emptyList(); } return Collections.unmodifiableList(extensions); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (autoWrapMessageAwareTransform ? 1231 : 1237); result = prime * result + (cacheMessageAsBytes ? 1231 : 1237); result = prime * result + (clientMode ? 1231 : 1237); result = prime * result + defaultQueueTimeout; result = prime * result + defaultTransactionTimeout; result = prime * result + maxQueueTransactionFilesSizeInMegabytes; result = prime * result + ((domainId == null) ? 0 : domainId.hashCode()); result = prime * result + (enableStreaming ? 1231 : 1237); result = prime * result + ((encoding == null) ? 0 : encoding.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + responseTimeout; result = prime * result + new Long(shutdownTimeout).hashCode(); result = prime * result + (synchronous ? 1231 : 1237); result = prime * result + ((systemModelType == null) ? 0 : systemModelType.hashCode()); result = prime * result + ((workingDirectory == null) ? 0 : workingDirectory.hashCode()); result = prime * result + (containerMode ? 1231 : 1237); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } DefaultMuleConfiguration other = (DefaultMuleConfiguration) obj; if (autoWrapMessageAwareTransform != other.autoWrapMessageAwareTransform) { return false; } if (cacheMessageAsBytes != other.cacheMessageAsBytes) { return false; } if (clientMode != other.clientMode) { return false; } if (defaultQueueTimeout != other.defaultQueueTimeout) { return false; } if (defaultTransactionTimeout != other.defaultTransactionTimeout) { return false; } if (domainId == null) { if (other.domainId != null) { return false; } } else if (!domainId.equals(other.domainId)) { return false; } if (enableStreaming != other.enableStreaming) { return false; } if (encoding == null) { if (other.encoding != null) { return false; } } else if (!encoding.equals(other.encoding)) { return false; } if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } if (responseTimeout != other.responseTimeout) { return false; } if (shutdownTimeout != other.shutdownTimeout) { return false; } if (synchronous != other.synchronous) { return false; } if (systemModelType == null) { if (other.systemModelType != null) { return false; } } else if (!systemModelType.equals(other.systemModelType)) { return false; } if (workingDirectory == null) { if (other.workingDirectory != null) { return false; } } else if (!workingDirectory.equals(other.workingDirectory)) { return false; } if (containerMode != other.containerMode) { return false; } if (maxQueueTransactionFilesSizeInMegabytes != other.maxQueueTransactionFilesSizeInMegabytes) { return false; } return true; } }