/*
* 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.tooling;
import static java.lang.String.format;
import static org.mule.runtime.deployment.model.internal.AbstractArtifactClassLoaderBuilder.PLUGIN_CLASSLOADER_IDENTIFIER;
import org.mule.runtime.deployment.model.api.plugin.ArtifactPluginDescriptor;
import org.mule.runtime.deployment.model.internal.plugin.PluginResolutionError;
import org.mule.runtime.module.artifact.classloader.ArtifactClassLoader;
import org.mule.runtime.module.artifact.classloader.ClassLoaderLookupPolicy;
import org.mule.runtime.module.artifact.classloader.RegionClassLoader;
import org.mule.runtime.module.artifact.classloader.ShutdownListener;
import org.mule.runtime.module.artifact.descriptor.ArtifactDescriptor;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;
/**
* Tooling {@link ClassLoader} that will delegate every call to it's delegate (the specific plugin class loader under
* {@link #delegatePluginClassLoader}, but when doing the {@link #dispose()} it will dispatch
* to the {@link RegionClassLoader} pointed by {@link #regionClassLoader} that contains all the plugins in it.
*
* @since 4.0
*/
public class ToolingPluginArtifactClassLoader implements ArtifactClassLoader {
private final RegionClassLoader regionClassLoader;
private final ArtifactClassLoader delegatePluginClassLoader;
/**
* Generates an instance of an {@link ArtifactClassLoader} if the parametrized {@code regionClassLoader} does contain
* within its {@link RegionClassLoader#getArtifactPluginClassLoaders()} the class loader responsible of handling the
* {@code artifactPluginDescriptor}.
*
* @param regionClassLoader class loader used to execute the {@link #dispose()} properly.
* @param artifactPluginDescriptor descriptor to look for within the {@link RegionClassLoader}.
*/
public ToolingPluginArtifactClassLoader(RegionClassLoader regionClassLoader,
ArtifactPluginDescriptor artifactPluginDescriptor) {
this.regionClassLoader = regionClassLoader;
this.delegatePluginClassLoader =
getPluginArtifactClassLoader(artifactPluginDescriptor, regionClassLoader.getArtifactPluginClassLoaders());
}
/**
* @param artifactPluginDescriptor to look for within the collection of {@link ArtifactClassLoader}s
* @param artifactPluginClassLoaders plugins class loaders to look for, at least one of them must contain the {@code pluginDescriptor}.
* @return the {@link ArtifactClassLoader} that was generated for the {@code artifactPluginDescriptor}
* @throws PluginResolutionError if the plugin wasn't found in the collection of {@code artifactPluginClassLoaders}
*/
private ArtifactClassLoader getPluginArtifactClassLoader(ArtifactPluginDescriptor artifactPluginDescriptor,
List<ArtifactClassLoader> artifactPluginClassLoaders) {
return artifactPluginClassLoaders.stream()
.filter(artifactClassLoader -> artifactClassLoader.getArtifactId()
.endsWith(PLUGIN_CLASSLOADER_IDENTIFIER + artifactPluginDescriptor.getName()))
.findFirst()
.orElseThrow(() -> new PluginResolutionError(format("Cannot generate a tooling ClassLoader as the region ClassLoader is missing the plugin '%s'",
artifactPluginDescriptor.getName())));
}
@Override
public String getArtifactId() {
return delegatePluginClassLoader.getArtifactId();
}
@Override
public <T extends ArtifactDescriptor> T getArtifactDescriptor() {
return delegatePluginClassLoader.getArtifactDescriptor();
}
@Override
public URL findResource(String resource) {
return delegatePluginClassLoader.findResource(resource);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException {
return delegatePluginClassLoader.findResources(name);
}
@Override
public Class<?> findLocalClass(String name) throws ClassNotFoundException {
return delegatePluginClassLoader.findLocalClass(name);
}
@Override
public ClassLoader getClassLoader() {
return delegatePluginClassLoader.getClassLoader();
}
@Override
public void addShutdownListener(ShutdownListener listener) {
delegatePluginClassLoader.addShutdownListener(listener);
}
@Override
public ClassLoaderLookupPolicy getClassLoaderLookupPolicy() {
return delegatePluginClassLoader.getClassLoaderLookupPolicy();
}
@Override
public URL findLocalResource(String resourceName) {
return delegatePluginClassLoader.findLocalResource(resourceName);
}
/**
* We want tooling believe the {@link ArtifactClassLoader} he's handling is the plugin's one, but we are actually shipping
* more than that with the {@link RegionClassLoader}.
* <p>
* So, to avoid any leaks the {@link #dispose()} of the plugin's {@link ArtifactClassLoader} is actually the one for the
* {@link RegionClassLoader}, which will eventually execute a {@link #dispose()} over the plugin's one.
*/
@Override
public void dispose() {
regionClassLoader.dispose();
}
}