/*
* 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.module.deployment.impl.internal.application;
import static java.lang.Boolean.getBoolean;
import static java.lang.String.format;
import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.mule.runtime.api.util.Preconditions.checkState;
import static org.mule.runtime.core.config.bootstrap.ArtifactType.APP;
import static org.mule.runtime.core.config.bootstrap.ArtifactType.POLICY;
import static org.mule.runtime.deployment.model.api.plugin.MavenClassLoaderConstants.MAVEN;
import static org.mule.runtime.module.artifact.classloader.MuleMavenPlugin.MULE_MAVEN_PLUGIN_ARTIFACT_ID;
import static org.mule.runtime.module.artifact.classloader.MuleMavenPlugin.MULE_MAVEN_PLUGIN_GROUP_ID;
import static org.mule.runtime.module.deployment.impl.internal.maven.MavenUtils.getPomModelFolder;
import org.mule.maven.client.api.LocalRepositorySupplierFactory;
import org.mule.maven.client.api.MavenClient;
import org.mule.runtime.api.exception.MuleRuntimeException;
import org.mule.runtime.api.i18n.I18nMessageFactory;
import org.mule.runtime.core.config.bootstrap.ArtifactType;
import org.mule.runtime.module.artifact.descriptor.BundleDependency;
import org.mule.runtime.module.artifact.descriptor.BundleDescriptor;
import org.mule.runtime.module.artifact.descriptor.ClassLoaderModel;
import org.mule.runtime.module.artifact.descriptor.ClassLoaderModel.ClassLoaderModelBuilder;
import org.mule.runtime.module.artifact.util.FileJarExplorer;
import org.mule.runtime.module.artifact.util.JarInfo;
import org.mule.runtime.module.deployment.impl.internal.maven.AbstractMavenClassLoaderModelLoader;
import java.io.File;
import java.net.MalformedURLException;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.maven.model.Build;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class is responsible of returning the {@link BundleDescriptor} of a given plugin's location and also creating a
* {@link ClassLoaderModel}
* <p>
* TODO(fernandezlautaro): MULE-11094 this class is the default implementation for discovering dependencies and URLs, which
* happens to be Maven based. There could be other ways to look for dependencies and URLs (probably for testing purposes where the
* plugins are done by hand and without maven) which will imply implementing the jira pointed out in this comment.
*
* @since 4.0
*/
public class DeployableMavenClassLoaderModelLoader extends AbstractMavenClassLoaderModelLoader {
public static final String ADD_TEST_DEPENDENCIES_KEY = "mule.embedded.maven.addTestDependenciesToAppClassPath";
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
public DeployableMavenClassLoaderModelLoader(MavenClient mavenClient,
LocalRepositorySupplierFactory localRepositorySupplierFactory) {
super(mavenClient, localRepositorySupplierFactory);
}
@Override
public String getId() {
return MAVEN;
}
@Override
protected void addArtifactSpecificClassloaderConfiguration(File artifactFile, ClassLoaderModelBuilder classLoaderModelBuilder,
Set<BundleDependency> dependencies) {
try {
classLoaderModelBuilder.containing(new File(artifactFile, "classes").toURL());
exportSharedLibrariesResourcesAndPackages(artifactFile, classLoaderModelBuilder, dependencies);
} catch (MalformedURLException e) {
throw new MuleRuntimeException(e);
}
}
private void exportSharedLibrariesResourcesAndPackages(File applicationFolder, ClassLoaderModelBuilder classLoaderModelBuilder,
Set<BundleDependency> dependencies) {
Model model = loadPomModel(applicationFolder);
Build build = model.getBuild();
if (build != null) {
List<Plugin> plugins = build.getPlugins();
if (plugins != null) {
Optional<Plugin> packagingPluginOptional =
plugins.stream().filter(plugin -> plugin.getArtifactId().equals(MULE_MAVEN_PLUGIN_ARTIFACT_ID)
&& plugin.getGroupId().equals(MULE_MAVEN_PLUGIN_GROUP_ID)).findFirst();
packagingPluginOptional.ifPresent(packagingPlugin -> {
Object configuration = packagingPlugin.getConfiguration();
if (configuration != null) {
Xpp3Dom sharedLibrariesDom = ((Xpp3Dom) configuration).getChild("sharedLibraries");
if (sharedLibrariesDom != null) {
Xpp3Dom[] sharedLibraries = sharedLibrariesDom.getChildren("sharedLibrary");
if (sharedLibraries != null) {
FileJarExplorer fileJarExplorer = new FileJarExplorer();
for (Xpp3Dom sharedLibrary : sharedLibraries) {
String groupId = getSharedLibraryAttribute(applicationFolder, sharedLibrary, "groupId");
String artifactId = getSharedLibraryAttribute(applicationFolder, sharedLibrary, "artifactId");
Optional<BundleDependency> bundleDependencyOptional = dependencies.stream()
.filter(bundleDependency -> bundleDependency.getDescriptor().getArtifactId().equals(artifactId)
&& bundleDependency.getDescriptor().getGroupId().equals(groupId))
.findFirst();
bundleDependencyOptional.map(bundleDependency -> {
JarInfo jarInfo = fileJarExplorer.explore(bundleDependency.getBundleUrl());
classLoaderModelBuilder.exportingPackages(jarInfo.getPackages());
classLoaderModelBuilder.exportingResources(jarInfo.getResources());
return bundleDependency;
}).orElseThrow(() -> new MuleRuntimeException(I18nMessageFactory
.createStaticMessage(format(
"Dependency %s:%s could not be found within the artifact %s. It must be declared within the maven dependencies of the artifact.",
groupId,
artifactId, applicationFolder.getName()))));
}
}
}
}
});
}
}
}
private String getSharedLibraryAttribute(File applicationFolder, Xpp3Dom sharedLibraryDom, String attributeName) {
Xpp3Dom attributeDom = sharedLibraryDom.getChild(attributeName);
checkState(attributeDom != null,
format("%s element was not defined within the shared libraries declared in the pom file of the artifact %s",
attributeName, applicationFolder.getName()));
String attributeValue = attributeDom.getValue().trim();
checkState(!isEmpty(attributeValue),
format("%s was defined but has an empty value within the shared libraries declared in the pom file of the artifact %s",
attributeName, applicationFolder.getName()));
return attributeValue;
}
@Override
protected boolean enabledTestDependencies() {
return getBoolean(ADD_TEST_DEPENDENCIES_KEY);
}
@Override
protected Model loadPomModel(File artifactFile) {
return getPomModelFolder(artifactFile);
}
@Override
public boolean supportsArtifactType(ArtifactType artifactType) {
return artifactType.equals(APP) || artifactType.equals(POLICY);
}
}