/* * 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.deployment.model.internal.domain; import static java.lang.String.format; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static org.apache.commons.io.FileUtils.listFiles; import static org.apache.commons.lang.StringUtils.isEmpty; import static org.apache.commons.lang.SystemUtils.LINE_SEPARATOR; import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage; import static org.mule.runtime.api.util.Preconditions.checkArgument; import static org.mule.runtime.container.api.MuleFoldersUtil.getDomainLibFolder; import static org.mule.runtime.deployment.model.api.domain.Domain.DEFAULT_DOMAIN_NAME; import static org.mule.runtime.module.artifact.classloader.ParentFirstLookupStrategy.PARENT_FIRST; import static org.mule.runtime.module.reboot.MuleContainerBootstrapUtils.getMuleDomainsDir; import org.mule.runtime.container.api.MuleFoldersUtil; import org.mule.runtime.deployment.model.api.DeploymentException; import org.mule.runtime.deployment.model.api.domain.DomainDescriptor; import org.mule.runtime.module.artifact.classloader.ArtifactClassLoader; import org.mule.runtime.module.artifact.classloader.ClassLoaderLookupPolicy; import org.mule.runtime.module.artifact.classloader.DeployableArtifactClassLoaderFactory; import org.mule.runtime.module.artifact.classloader.LookupStrategy; import org.mule.runtime.module.artifact.classloader.ShutdownListener; import org.mule.runtime.module.artifact.descriptor.ArtifactDescriptor; import org.mule.runtime.module.artifact.util.FileJarExplorer; import org.mule.runtime.module.artifact.util.JarExplorer; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Creates {@link ArtifactClassLoader} for domain artifacts. */ public class DomainClassLoaderFactory implements DeployableArtifactClassLoaderFactory<DomainDescriptor> { protected static final Logger logger = LoggerFactory.getLogger(DomainClassLoaderFactory.class); private final ClassLoader parentClassLoader; private Map<String, ArtifactClassLoader> domainArtifactClassLoaders = new HashMap<>(); private JarExplorer jarExplorer = new FileJarExplorer(); /** * Creates a new instance * * @param parentClassLoader parent classLoader of the created instance. Can be null. */ public DomainClassLoaderFactory(ClassLoader parentClassLoader) { checkArgument(parentClassLoader != null, "parentClassLoader cannot be null"); this.parentClassLoader = parentClassLoader; } public void setJarExplorer(JarExplorer jarExplorer) { this.jarExplorer = jarExplorer; } /** * @param domainName name of the domain. Non empty. * @return the unique identifier for the domain in the container. */ public static String getDomainId(String domainName) { checkArgument(!isEmpty(domainName), "domainName cannot be empty"); return "domain/" + domainName; } @Override public ArtifactClassLoader create(String artifactId, ArtifactClassLoader parent, DomainDescriptor descriptor, List<ArtifactClassLoader> artifactClassLoaders) { String domainId = getDomainId(descriptor.getName()); ArtifactClassLoader domainClassLoader = domainArtifactClassLoaders.get(domainId); if (domainClassLoader != null) { return domainClassLoader; } else { synchronized (this) { domainClassLoader = domainArtifactClassLoaders.get(domainId); if (domainClassLoader == null) { if (descriptor.getName().equals(DEFAULT_DOMAIN_NAME)) { domainClassLoader = getDefaultDomainClassLoader(parent.getClassLoaderLookupPolicy()); } else { domainClassLoader = getCustomDomainClassLoader(parent.getClassLoaderLookupPolicy(), descriptor); } domainArtifactClassLoaders.put(domainId, domainClassLoader); } } } return domainClassLoader; } private ArtifactClassLoader getCustomDomainClassLoader(ClassLoaderLookupPolicy containerLookupPolicy, DomainDescriptor domain) { validateDomain(domain.getName()); final List<URL> urls = getDomainUrls(domain.getName()); final Map<String, LookupStrategy> domainLookStrategies = getLookStrategiesFrom(urls); final ClassLoaderLookupPolicy domainLookupPolicy = containerLookupPolicy.extend(domainLookStrategies); ArtifactClassLoader classLoader = new MuleSharedDomainClassLoader(domain, parentClassLoader, domainLookupPolicy, urls); return createClassLoaderUnregisterWrapper(classLoader); } private Map<String, LookupStrategy> getLookStrategiesFrom(List<URL> libraries) { final Map<String, LookupStrategy> result = new HashMap<>(); for (URL library : libraries) { Set<String> packages = jarExplorer.explore(library).getPackages(); for (String packageName : packages) { result.put(packageName, PARENT_FIRST); } } return result; } private List<URL> getDomainUrls(String domain) throws DeploymentException { try { List<URL> urls = new LinkedList<>(); urls.add(MuleFoldersUtil.getDomainFolder(domain).toURI().toURL()); File domainLibraryFolder = getDomainLibFolder(domain); if (domainLibraryFolder.exists()) { Collection<File> jars = listFiles(domainLibraryFolder, new String[] {"jar"}, false); if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Loading Shared ClassLoader Domain: ").append(domain).append(LINE_SEPARATOR); sb.append("=============================").append(LINE_SEPARATOR); for (File jar : jars) { sb.append(jar.toURI().toURL()).append(LINE_SEPARATOR); } sb.append("=============================").append(LINE_SEPARATOR); logger.debug(sb.toString()); } for (File jar : jars) { urls.add(jar.toURI().toURL()); } } return urls; } catch (MalformedURLException e) { throw new DeploymentException(createStaticMessage(format("Cannot read domain '%s' libraries", domain)), e); } } private ArtifactClassLoader getDefaultDomainClassLoader(ClassLoaderLookupPolicy containerLookupPolicy) { return new MuleSharedDomainClassLoader(new DomainDescriptor(DEFAULT_DOMAIN_NAME), parentClassLoader, containerLookupPolicy.extend(emptyMap()), emptyList()); } private void validateDomain(String domain) { File domainFolder = new File(getMuleDomainsDir(), domain); if (!(domainFolder.exists() && domainFolder.isDirectory())) { throw new DeploymentException(createStaticMessage(format("Domain %s does not exists", domain))); } } private ArtifactClassLoader createClassLoaderUnregisterWrapper(final ArtifactClassLoader classLoader) { return new ArtifactClassLoader() { @Override public String getArtifactId() { return classLoader.getArtifactId(); } @Override public <T extends ArtifactDescriptor> T getArtifactDescriptor() { return classLoader.getArtifactDescriptor(); } @Override public URL findResource(String resource) { return classLoader.findResource(resource); } @Override public Enumeration<URL> findResources(String name) throws IOException { return classLoader.findResources(name); } @Override public Class<?> findLocalClass(String name) throws ClassNotFoundException { return classLoader.findLocalClass(name); } @Override public URL findLocalResource(String resource) { return classLoader.findLocalResource(resource); } @Override public ClassLoader getClassLoader() { return classLoader.getClassLoader(); } @Override public void dispose() { domainArtifactClassLoaders.remove(classLoader.getArtifactId()); classLoader.dispose(); } @Override public void addShutdownListener(ShutdownListener listener) { classLoader.addShutdownListener(listener); } @Override public ClassLoaderLookupPolicy getClassLoaderLookupPolicy() { return classLoader.getClassLoaderLookupPolicy(); } }; } }