package com.github.chrisdchristo.capsule;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.DefaultMavenProjectHelper;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.util.IOUtil;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
/**
* Super class with generic methods
*/
public abstract class Mojo extends AbstractMojo {
/**
* AETHER REPO LINK
*/
@Component
RepositorySystem repoSystem = null;
@Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
RepositorySystemSession repoSession = null;
@Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true)
List<RemoteRepository> remoteRepos = null;
@Parameter(defaultValue = "${project.build.finalName}", readonly = true)
String finalName = null;
@Parameter(defaultValue = "${project.build.directory}")
File buildDir = null;
@Parameter(defaultValue = "${project.basedir}")
File baseDir = null;
/**
* Project
*/
@Parameter(defaultValue = "${project}", readonly = true)
MavenProject project = null;
final MavenProjectHelper helper = new DefaultMavenProjectHelper();
abstract String pluginKey();
abstract String logPrefix();
// DEPENDENCIES
Set<Dependency> appDependencies() { return set(project.getTestDependencies()); }
Set<Dependency> appDirectDependencies() { return set(project.getDependencies()); }
Set<Artifact> appDependencyArtifacts() { return project.getArtifacts(); }
Set<Artifact> appDirectDependencyArtifacts() { return project.getDependencyArtifacts(); }
Set<Dependency> pluginDependencies() { return getDependenciesOf(set(plugin().getDependencies()), true); }
Set<Dependency> pluginDirectDependencies() { return set(plugin().getDependencies()); }
Set<Artifact> pluginDependencyArtifacts() { return getDependencyArtifactsOf(set(plugin().getDependencies()), true); }
Set<Artifact> pluginDirectDependencyArtifacts() { return toArtifacts(set(plugin().getDependencies())); }
// RESOLVERS
private Plugin plugin() {
return project.getPlugin(pluginKey());
}
private ArtifactResult resolve(final Dependency dependency) {
return resolve(dependency.getGroupId(), dependency.getArtifactId(), dependency.getClassifier(), dependency.getVersion());
}
ArtifactResult resolve(final String groupId, final String artifactId, final String classifier, final String version) {
return resolve(coords(groupId, artifactId, classifier, version));
}
ArtifactResult resolve(final String coords) {
try {
return repoSystem.resolveArtifact(repoSession, new ArtifactRequest(new DefaultArtifact(coords), remoteRepos, null));
} catch (final ArtifactResolutionException e) {
warn("\t\t[Resolve] Failed to resolve: [" + coords + "]");
return null;
}
}
private Set<ArtifactResult> resolveDependencies(final Dependency dependency) {
try {
final CollectRequest collectRequest = new CollectRequest(new org.eclipse.aether.graph.Dependency(resolve(dependency).getArtifact(), ""), remoteRepos);
return set(repoSystem.resolveDependencies(repoSession, new DependencyRequest(collectRequest, null)).getArtifactResults());
} catch (final DependencyResolutionException e) {
warn("\t\t[Resolve] Failed to resolve: [" + coords(dependency) + "]");
return new HashSet<>();
}
}
Artifact toArtifact(final ArtifactResult ar) {
if (ar == null) return null;
final Artifact artifact = new org.apache.maven.artifact.DefaultArtifact(
ar.getArtifact().getGroupId(),
ar.getArtifact().getArtifactId(),
ar.getArtifact().getVersion(),
null,
"jar",
ar.getArtifact().getClassifier(),
null);
if (ar.getRequest().getDependencyNode() != null && ar.getRequest().getDependencyNode().getDependency() != null) {
artifact.setScope(ar.getRequest().getDependencyNode().getDependency().getScope());
artifact.setOptional(ar.getRequest().getDependencyNode().getDependency().isOptional());
}
if (artifact.getScope() == null || artifact.getScope().isEmpty()) artifact.setScope("compile");
artifact.setFile(ar.getArtifact().getFile());
return artifact;
}
private Artifact toArtifact(final Dependency dependency) {
if (dependency == null) return null;
final Artifact artifact = toArtifact(resolve(dependency));
artifact.setScope(dependency.getScope());
if (artifact.getScope() == null || artifact.getScope().isEmpty()) artifact.setScope("compile");
return artifact;
}
private Dependency toDependency(final Artifact artifact) {
if (artifact == null) return null;
final Dependency dependency = new Dependency();
dependency.setGroupId(artifact.getGroupId());
dependency.setArtifactId(artifact.getArtifactId());
dependency.setVersion(artifact.getVersion());
dependency.setScope(artifact.getScope());
dependency.setClassifier(artifact.getClassifier());
dependency.setOptional(artifact.isOptional());
if (dependency.getScope() == null || dependency.getScope().isEmpty()) dependency.setScope("compile");
return dependency;
}
private Dependency toDependency(final ArtifactResult ar) {
return toDependency(toArtifact(ar));
}
private Set<Artifact> getDependencyArtifactsOf(final Dependency dependency, final boolean includeRoot) {
final Set<Artifact> artifacts = new HashSet<>();
if (includeRoot) artifacts.add(toArtifact(dependency));
for (final ArtifactResult ar : resolveDependencies(dependency)) {
final Artifact artifact = toArtifact(ar);
// if null set to default compile
if (artifact.getScope() == null || artifact.getScope().isEmpty()) artifact.setScope("compile");
// skip any deps that aren't compile or runtime
if (!artifact.getScope().equals("compile") && !artifact.getScope().equals("runtime")) continue;
// set direct-scope on transitive deps
if (dependency.getScope().equals("provided")) artifact.setScope("provided");
if (dependency.getScope().equals("system")) artifact.setScope("system");
if (dependency.getScope().equals("test")) artifact.setScope("test");
artifacts.add(toArtifact(ar));
}
return cleanArtifacts(artifacts);
}
private Set<Artifact> getDependencyArtifactsOf(final Set<Dependency> dependencies, final boolean includeRoot) {
final Set<Artifact> artifacts = new HashSet<>();
for (final Dependency dependency : dependencies) {
artifacts.addAll(getDependencyArtifactsOf(dependency, includeRoot));
}
return cleanArtifacts(artifacts);
}
private Set<Dependency> getDependenciesOf(final Dependency dependency, final boolean includeRoot) {
final Set<Dependency> dependencies = new HashSet<>();
if (includeRoot) dependencies.add(dependency);
for (final ArtifactResult ar : resolveDependencies(dependency)) {
dependencies.add(toDependency(ar));
}
return cleanDependencies(dependencies);
}
private Set<Dependency> getDependenciesOf(final Set<Dependency> dependencies, final boolean includeRoot) {
final Set<Dependency> dependenciesAll = new HashSet<>();
for (final Dependency dependency : dependencies) {
dependenciesAll.addAll(getDependenciesOf(dependency, includeRoot));
}
return cleanDependencies(dependenciesAll);
}
private Set<Artifact> toArtifacts(final Set<Dependency> dependencies) {
final Set<Artifact> artifacts = new HashSet<>();
for (final Dependency dependency : dependencies) {
artifacts.add(toArtifact(dependency));
}
return cleanArtifacts(artifacts);
}
// JAR & FILE HELPERS
String addDirectoryToJar(final JarOutputStream jar, final String outputDirectory) throws IOException {
// format the output directory
String formattedOutputDirectory = "";
if (outputDirectory != null && !outputDirectory.isEmpty()) {
if (!outputDirectory.endsWith("/")) {
formattedOutputDirectory = outputDirectory + File.separatorChar;
} else {
formattedOutputDirectory = outputDirectory;
}
}
if (!formattedOutputDirectory.isEmpty()) {
try {
jar.putNextEntry(new ZipEntry(formattedOutputDirectory));
jar.closeEntry();
} catch (final ZipException ignore) {} // ignore duplicate entries and other errors
}
return formattedOutputDirectory;
}
JarOutputStream addToJar(final String name, final InputStream input, final JarOutputStream jar) throws IOException {
try {
debug("\t[Added to Jar]: " + name);
jar.putNextEntry(new ZipEntry(name));
IOUtil.copy(input, jar);
jar.closeEntry();
} catch (final ZipException ignore) {} // ignore duplicate entries and other errors
IOUtil.close(input);
return jar;
}
// LOG
void debug(final String message) { getLog().debug(logPrefix() + message); }
void info(final String message) { getLog().info(logPrefix() + message); }
void warn(final String message) { getLog().warn(logPrefix() + message); }
void printManifest(final Manifest manifest) {
info("\t[Manifest]:");
for (final Map.Entry<Object, Object> attr : manifest.getMainAttributes().entrySet()) {
info("\t\t" + attr.getKey().toString() + ": " + attr.getValue().toString());
}
for (final Map.Entry<String, Attributes> entry : manifest.getEntries().entrySet()) {
info("");
info("\t\tName:" + entry.getKey());
for (final Map.Entry<Object, Object> attr : entry.getValue().entrySet()) {
info("\t\t" + attr.getKey().toString() + ": " + attr.getValue().toString());
}
}
}
// HELPERS
public static class Pair<K, V> {
public K key;
public V value;
public Pair() {}
public Pair(final K key, final V value) {
this.key = key;
this.value = value;
}
}
static <T> Set<T> set(final List<T> list) {
return new HashSet<>(list);
}
static <T> Set<T> set(final Enumeration<T> enumeration) {
final Set<T> set = new HashSet<>();
while (enumeration.hasMoreElements())
set.add(enumeration.nextElement());
return set;
}
// COORDS
static String coords(final String groupId, final String artifactId, final String classifier, final String version) {
final StringBuilder coords = new StringBuilder();
coords.append(groupId).append(":").append(artifactId);
if (classifier != null && !classifier.isEmpty())
coords.append(":").append(classifier);
if (version != null && !version.isEmpty())
coords.append(":").append(version);
return coords.toString();
}
static String coords(final Artifact artifact) {
return coords(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), artifact.getVersion());
}
static String coords(final Dependency dependency) {
return coords(dependency.getGroupId(), dependency.getArtifactId(), dependency.getClassifier(), dependency.getVersion());
}
static String coordsWithExclusions(final Dependency dependency) {
final StringBuilder coords = new StringBuilder(coords(dependency));
if (dependency.getExclusions().size() > 0) {
final StringBuilder exclusionsList = new StringBuilder();
int i = 0;
for (final Exclusion exclusion : dependency.getExclusions()) {
if (i > 0) exclusionsList.append(",");
exclusionsList.append(exclusion.getGroupId()).append(":").append(exclusion.getArtifactId());
i++;
}
coords.append("(").append(exclusionsList.toString()).append(")");
}
return coords.toString();
}
// CLEANERS
// clean any duplicates
static Set<Dependency> cleanDependencies(final Set<Dependency> dependencies) {
final Set<Dependency> dependenciesClean = new HashSet<>();
for (final Dependency dependencyA : dependencies) {
boolean found = false;
for (final Dependency dependencyB : dependenciesClean) {
if (coords(dependencyA).equals(coords(dependencyB))) {
found = true;
break;
}
}
if (!found) dependenciesClean.add(dependencyA);
}
return dependenciesClean;
}
// clean any duplicates
static Set<Artifact> cleanArtifacts(final Set<Artifact> artifacts) {
final Set<Artifact> artifactsClean = new HashSet<>();
for (final Artifact artifactA : artifacts) {
boolean found = false;
for (final Artifact artifactB : artifactsClean) {
if (coords(artifactA).equals(coords(artifactB))){
found = true;
break;
}
}
if (!found) artifactsClean.add(artifactA);
}
return artifactsClean;
}
static Set<Dependency> cleanDependencies(final Set<Dependency> setA, final boolean includeA, final Set<Dependency> setB, final boolean includeB) {
final Set<Dependency> set = new HashSet<>();
if (includeA) set.addAll(setA);
if (includeB) set.addAll(setB);
return cleanDependencies(set);
}
static Set<Artifact> cleanArtifacts(final Set<Artifact> setA, final boolean includeA, final Set<Artifact> setB, final boolean includeB) {
final Set<Artifact> set = new HashSet<>();
if (includeA) set.addAll(setA);
if (includeB) set.addAll(setB);
return cleanArtifacts(set);
}
}