From 9476c4a28c1cc1767606f90c4142c4254694fd34 Mon Sep 17 00:00:00 2001 From: Bob McWhirter Date: Fri, 13 Nov 2015 12:22:17 -0500 Subject: [PATCH] Break out the dependency-scrobbling bits from BuildTool and possibly be smarter about gathering stuff. --- .../util/MavenArtifactDescriptor.java | 6 +- .../util/WildFlySwarmBootstrapConf.java | 5 +- pom.xml | 1 + .../org/wildfly/swarm/tools/ArtifactSpec.java | 7 +- .../org/wildfly/swarm/tools/BuildTool.java | 396 ++-------------- .../swarm/tools/DependencyManager.java | 434 ++++++++++++++++++ .../swarm/tools/DependencyManagerTest.java | 278 +++++++++++ .../swarm/tools/MockArtifactResolver.java | 58 +++ 8 files changed, 817 insertions(+), 368 deletions(-) create mode 100644 tools/src/main/java/org/wildfly/swarm/tools/DependencyManager.java create mode 100644 tools/src/test/java/org/wildfly/swarm/tools/DependencyManagerTest.java create mode 100644 tools/src/test/java/org/wildfly/swarm/tools/MockArtifactResolver.java diff --git a/bootstrap/src/main/java/org/wildfly/swarm/bootstrap/util/MavenArtifactDescriptor.java b/bootstrap/src/main/java/org/wildfly/swarm/bootstrap/util/MavenArtifactDescriptor.java index 08935cafb9..5a7fa77eda 100644 --- a/bootstrap/src/main/java/org/wildfly/swarm/bootstrap/util/MavenArtifactDescriptor.java +++ b/bootstrap/src/main/java/org/wildfly/swarm/bootstrap/util/MavenArtifactDescriptor.java @@ -2,8 +2,6 @@ import java.io.File; import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; /** * @author Bob McWhirter @@ -156,10 +154,10 @@ public String mavenGav() { this.version; } - public String repoPath(boolean forClasspath) { + public String repoPath(boolean forJar) { char delim = File.separatorChar; - if (forClasspath) { + if (forJar) { delim = '/'; } diff --git a/bootstrap/src/main/java/org/wildfly/swarm/bootstrap/util/WildFlySwarmBootstrapConf.java b/bootstrap/src/main/java/org/wildfly/swarm/bootstrap/util/WildFlySwarmBootstrapConf.java index 46afaf6e71..df4f8ad8b0 100644 --- a/bootstrap/src/main/java/org/wildfly/swarm/bootstrap/util/WildFlySwarmBootstrapConf.java +++ b/bootstrap/src/main/java/org/wildfly/swarm/bootstrap/util/WildFlySwarmBootstrapConf.java @@ -37,6 +37,7 @@ public void addEntry(MavenArtifactDescriptor entry) { this.entries.add( entry ); } + public void addEntry(String gav) throws IOException { String[] parts = gav.split(":"); @@ -55,8 +56,8 @@ public void addEntry(String groupId, String artifactId, String type, String clas this.entries.add(new MavenArtifactDescriptor(groupId, artifactId, type, classifier, version)); } - public List getEntries() { - return Collections.unmodifiableList(this.entries); + public List getEntries() { + return Collections.unmodifiableList( this.entries); } public WildFlySwarmBootstrapConf(InputStream in) throws IOException { diff --git a/pom.xml b/pom.xml index d996c5a939..68a4369cbf 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,7 @@ 127.0.0.1 ${maven.repo.local} + ${project.version} diff --git a/tools/src/main/java/org/wildfly/swarm/tools/ArtifactSpec.java b/tools/src/main/java/org/wildfly/swarm/tools/ArtifactSpec.java index ca76a5d388..5e0e56f67b 100644 --- a/tools/src/main/java/org/wildfly/swarm/tools/ArtifactSpec.java +++ b/tools/src/main/java/org/wildfly/swarm/tools/ArtifactSpec.java @@ -16,7 +16,6 @@ package org.wildfly.swarm.tools; import java.io.File; -import java.io.IOException; import org.wildfly.swarm.bootstrap.util.MavenArtifactDescriptor; @@ -33,14 +32,16 @@ public class ArtifactSpec extends MavenArtifactDescriptor { public boolean gathered = false; - public static ArtifactSpec fromMscGav(String gav) throws IOException { + public boolean bootstrapped = false; + + public static ArtifactSpec fromMscGav(String gav) { String[] parts = gav.split(":"); if (parts.length == 3) { return new ArtifactSpec("compile", parts[0], parts[1], parts[2], "jar", null, null); } else if (parts.length == 4) { return new ArtifactSpec("compile", parts[0], parts[1], parts[2], "jar", parts[3], null); } else { - throw new IOException("Invalid gav: " + gav); + throw new RuntimeException("Invalid gav: " + gav); } } diff --git a/tools/src/main/java/org/wildfly/swarm/tools/BuildTool.java b/tools/src/main/java/org/wildfly/swarm/tools/BuildTool.java index 36f741c1e0..f0bf84b859 100644 --- a/tools/src/main/java/org/wildfly/swarm/tools/BuildTool.java +++ b/tools/src/main/java/org/wildfly/swarm/tools/BuildTool.java @@ -15,39 +15,24 @@ */ package org.wildfly.swarm.tools; -import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Enumeration; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Properties; import java.util.Set; -import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.ZipEntry; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.ByteArrayAsset; -import org.jboss.shrinkwrap.api.asset.FileAsset; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.exporter.ZipExporter; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.impl.base.asset.ZipFileEntryAsset; -import org.wildfly.swarm.bootstrap.util.MavenArtifactDescriptor; import org.wildfly.swarm.bootstrap.util.WildFlySwarmApplicationConf; import org.wildfly.swarm.bootstrap.util.WildFlySwarmBootstrapConf; import org.wildfly.swarm.bootstrap.util.WildFlySwarmDependenciesConf; @@ -67,27 +52,15 @@ public class BuildTool { private boolean resolveTransitiveDependencies = false; - private final Set dependencies = new HashSet<>(); - private final Set moduleDependencies = new HashSet<>(); + private DependencyManager dependencyManager = new DependencyManager(); private final Set resourceDirectories = new HashSet<>(); - //private ArtifactSpec projectArtifact; private ProjectAsset projectAsset; - private ArtifactResolvingHelper resolver; - private Properties properties = new Properties(); - private Set bootstrappedArtifacts = new HashSet<>(); - - private Set coreSwarmArtifacts = new HashSet<>(); - - private Set bootstrappedModules = new HashSet<>(); - - private Map providedMappings = new HashMap<>(); - private Set additionnalModules = new HashSet<>(); public BuildTool() { @@ -130,24 +103,16 @@ public BuildTool projectArchive(Archive archive) { } public BuildTool dependency(String scope, String groupId, String artifactId, String version, String packaging, String classifier, File file) { - this.dependencies.add(new ArtifactSpec(scope, groupId, artifactId, version, packaging, classifier, file)); + this.dependencyManager.addDependency(new ArtifactSpec(scope, groupId, artifactId, version, packaging, classifier, file)); return this; } - public Set dependencies() { - return this.dependencies; - } - - public Set moduleDependencies() { - return this.moduleDependencies; - } - public Set additionnalModules() { return this.additionnalModules; } public BuildTool artifactResolvingHelper(ArtifactResolvingHelper resolver) { - this.resolver = resolver; + this.dependencyManager.setArtifactResolvingHelper(resolver); return this; } @@ -162,176 +127,38 @@ public File build(String baseName, Path dir) throws Exception { } public Archive build() throws Exception { - resolveDependencies(); + analyzeDependencies(); addWildflySwarmBootstrapJar(); - setupBootstrap(); - createManifest(); - createWildflySwarmProperties(); - collectDependencies(); - setupApplication(); - createDependenciesTxt(); - addAdditionnalModule(); + addWildFlyBootstrapConf(); + addManifest(); + addWildFlySwarmProperties(); + addWildFlySwarmApplicationConf(); + addWildFlySwarmDependenciesConf(); + addAdditionnalModules(); + populateUberJarMavenRepository(); return this.archive; } - private void addWildflySwarmBootstrapJar() throws BuildException, IOException { - ArtifactSpec artifact = findArtifact("org.wildfly.swarm", "wildfly-swarm-bootstrap", null, "jar", null); + protected void analyzeDependencies() throws Exception { + this.dependencyManager.analyzeDependencies(this.resolveTransitiveDependencies); + } - this.bootstrappedArtifacts.add( artifact ); + private void addWildflySwarmBootstrapJar() throws BuildException, IOException { + ArtifactSpec artifact = this.dependencyManager.findWildFlySwarmBootstrapJar(); if (!bootstrapJarShadesJBossModules(artifact.file)) { - ArtifactSpec jbossModules = findArtifact("org.jboss.modules", "jboss-modules", null, "jar", null); + ArtifactSpec jbossModules = this.dependencyManager.findJBossModulesJar(); expandArtifact(jbossModules.file); } expandArtifact(artifact.file); } - - private void setupBootstrap() throws Exception { - for (ArtifactSpec each : this.dependencies) { - try (JarFile jar = new JarFile(each.file)) { - ZipEntry entry = jar.getEntry("wildfly-swarm-bootstrap.conf"); - if (entry != null) { - this.bootstrappedArtifacts.add(each); - - try (InputStream in = jar.getInputStream(entry)) { - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String line = null; - - while ((line = reader.readLine()) != null) { - line = line.trim(); - if (!line.isEmpty()) { - this.bootstrappedModules.add(line); - } - } - } - } - } - } - - WildFlySwarmBootstrapConf bootstrapConf = new WildFlySwarmBootstrapConf(); - - for (ArtifactSpec each : this.bootstrappedArtifacts) { - bootstrapConf.addEntry( each ); - gatherDependency(each); - } - - this.archive.add(new StringAsset(bootstrapConf.toString()), WildFlySwarmBootstrapConf.CLASSPATH_LOCATION ); - } - - private void setupApplication() throws Exception { - - Set applicationArtifacts = new HashSet<>(); - - for (ArtifactSpec each : this.dependencies) { - if (!this.bootstrappedArtifacts.contains(each) ) { - if (each.type().equals("jar") && each.shouldGather) { - applicationArtifacts.add(each); - } - } - } - - this.archive.add(this.projectAsset); - - WildFlySwarmApplicationConf appConf = new WildFlySwarmApplicationConf(); - - for (String each : this.bootstrappedModules) { - appConf.addEntry(new WildFlySwarmApplicationConf.ModuleEntry(each)); - } - - for (ArtifactSpec each : applicationArtifacts) { - String mapped = this.providedMappings.get(each.groupId() + ":" + each.artifactId()); - if (mapped != null) { - appConf.addEntry(new WildFlySwarmApplicationConf.ModuleEntry(mapped)); - } else { - if (includeAsBootstrapJar(each)) { - gatherDependency(each); - appConf.addEntry(new WildFlySwarmApplicationConf.GAVEntry(each)); - } - } - } - - appConf.addEntry( new WildFlySwarmApplicationConf.PathEntry( this.projectAsset.getName())); - this.archive.add(new StringAsset(appConf.toString()), WildFlySwarmApplicationConf.CLASSPATH_LOCATION ); - - } - - public boolean includeAsBootstrapJar(ArtifactSpec dependency) { - - if ( dependency.scope.equals( "TEST" ) ) { - return false; - } - if (dependency.groupId().equals("org.jboss.modules") && dependency.artifactId().equals("jboss-modules")) { - return false; - } - - return !dependency.scope.equals("PROVIDED"); - - //return true; - } - - protected boolean hasNonBootstrapMarker(ArtifactSpec spec) { - if (spec.file != null) { - - try (JarFile jar = new JarFile(spec.file)) { - ZipEntry entry = jar.getEntry("META-INF/wildfly-swarm-non-bootstrap.txt"); - return (entry != null); - } catch (IOException e) { - //e.printStackTrace(); - } - - } - - return false; - } - - protected void resolveDependencies() throws Exception { - if (this.resolveTransitiveDependencies) { - Set newDeps = this.resolver.resolveAll(dependencies); - this.dependencies.clear(); - this.dependencies.addAll(newDeps); - } else { - for (ArtifactSpec each : dependencies()) { - resolveArtifact(each); - } - } - } - - protected ArtifactSpec resolveArtifact(ArtifactSpec spec) throws Exception { - if (spec.file == null) { - ArtifactSpec newArtifact = this.resolver.resolve(spec); - - if (newArtifact == null) { - throw new BuildException("Unable to resolve artifact: " + spec); - } - - spec.file = newArtifact.file; - } - - return spec; - } - - protected void gatherDependency(ArtifactSpec artifact) throws Exception { - if ( artifact.gathered ) { - return; - } - artifact = resolveArtifact(artifact); - - StringBuilder artifactPath = new StringBuilder("m2repo/"); - artifactPath.append( artifact.repoPath(true)); - - this.archive.add(new FileAsset(artifact.file), artifactPath.toString()); - - artifact.gathered = true; - } - - private void createManifest() throws IOException { + private void addManifest() throws IOException { UberJarManifestAsset manifest = new UberJarManifestAsset(this.mainClass); - this.archive.add( manifest ); + this.archive.add(manifest); } - private void createWildflySwarmProperties() throws IOException { - + private void addWildFlySwarmProperties() throws IOException { Properties props = new Properties(); Enumeration propNames = this.properties.propertyNames(); @@ -341,9 +168,6 @@ private void createWildflySwarmProperties() throws IOException { String eachValue = this.properties.get(eachName).toString(); props.put(eachName, eachValue); } - //props.putAll( this.properties ); - - //props.setProperty("wildfly.swarm.app.artifact", this.projectArtifact.artifactId + "-" + this.projectArtifact.version + "." + this.projectArtifact.packaging); props.setProperty("wildfly.swarm.app.artifact", this.projectAsset.getSimpleName()); props.setProperty("wildfly.swarm.context.path", this.contextPath); @@ -354,148 +178,24 @@ private void createWildflySwarmProperties() throws IOException { } - private void createDependenciesTxt() throws IOException { - Set provided = new HashSet<>(); - - for (ArtifactSpec each : this.dependencies) { - if (each.type().equals("jar")) { - try (JarFile jar = new JarFile(each.file)) { - - ZipEntry entry = jar.getEntry("provided-dependencies.txt"); - if (entry != null) { - // add ourselves - provided.add(each.groupId() + ":" + each.artifactId()); - - try (InputStream in = jar.getInputStream(entry)) { - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String line = null; - - // add everything mentioned in the file - while ((line = reader.readLine()) != null) { - line = line.trim(); - if (line.length() > 0) { - String[] parts = line.split("\\|"); - if (parts.length > 1) { - this.providedMappings.put(parts[0], parts[1]); - } - provided.add(parts[0].trim()); - } - } - } - } - } - } - } - - for (String each : this.resourceDirectories) { - Path providedDependencies = Paths.get(each, "provided-dependencies.txt"); - if (Files.exists(providedDependencies)) { - - try (InputStream in = new FileInputStream(providedDependencies.toFile())) { - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String line = null; - - // add everything mentioned in the file - while ((line = reader.readLine()) != null) { - line = line.trim(); - if (line.length() > 0) { - String[] parts = line.split("\\|"); - if (parts.length > 1) { - this.providedMappings.put(parts[0], parts[1]); - } - provided.add(parts[0].trim()); - } - } - } - } - } - - StringBuilder depsTxt = new StringBuilder(); - StringBuilder extraDepsTxt = new StringBuilder(); - - WildFlySwarmDependenciesConf depsConf = new WildFlySwarmDependenciesConf(); - - for (ArtifactSpec each : this.dependencies) { - if (provided.contains(each.groupId() + ":" + each.artifactId())) { - continue; - } - if (each.scope.equals("compile")) { - if (each.type().equals("jar")) { - depsConf.addPrimaryDependency( each ); - } else { - depsConf.addExtraDependency( each ); - } - } - - this.archive.add(new StringAsset(depsConf.toString()), WildFlySwarmDependenciesConf.CLASSPATH_LOCATION ); - } - } - - protected void collectDependencies() throws Exception { - if (!this.bundleDependencies) { - return; - } - analyzeModuleDependencies(); - gatherDependencies(); - } - - - protected void analyzeModuleDependencies() throws IOException { - for (ArtifactSpec each : this.bootstrappedArtifacts) { - this.coreSwarmArtifacts.add(each); - analyzeModuleDependencies(each); - } + private void addWildFlyBootstrapConf() throws Exception { + WildFlySwarmBootstrapConf bootstrapConf = this.dependencyManager.getWildFlySwarmBootstrapConf(); + this.archive.add(new StringAsset(bootstrapConf.toString()), WildFlySwarmBootstrapConf.CLASSPATH_LOCATION); } - protected void analyzeModuleDependencies(ArtifactSpec artifact) throws IOException { - if (!artifact.type().equals("jar")) { - return; - } - - JarFile jar = new JarFile(artifact.file); - Enumeration entries = jar.entries(); - - while (entries.hasMoreElements()) { - JarEntry each = entries.nextElement(); - String name = each.getName(); - if (name.startsWith("modules/") && name.endsWith("module.xml")) { - try (InputStream in = jar.getInputStream(each)) { - analyzeModuleDependencies(in); - } - } - } + private void addWildFlySwarmDependenciesConf() throws IOException { + WildFlySwarmDependenciesConf depsConf = this.dependencyManager.getWildFlySwarmDependenciesConf(); + this.archive.add(new StringAsset(depsConf.toString()), WildFlySwarmDependenciesConf.CLASSPATH_LOCATION); } - protected void analyzeModuleDependencies(InputStream moduleXml) throws IOException { - ModuleAnalyzer analyzer = new ModuleAnalyzer(moduleXml); - this.moduleDependencies.addAll( analyzer.getDependencies() ); + private void addWildFlySwarmApplicationConf() throws Exception { + WildFlySwarmApplicationConf appConf = this.dependencyManager.getWildFlySwarmApplicationConf(this.projectAsset); + this.archive.add(new StringAsset(appConf.toString()), WildFlySwarmApplicationConf.CLASSPATH_LOCATION); + this.archive.add(this.projectAsset); } - protected void gatherDependencies() throws Exception { - this.coreSwarmArtifacts.addAll(this.moduleDependencies); - - if (this.projectAsset.getSimpleName().endsWith(".war")) { - for (ArtifactSpec each : this.dependencies) { - if (!this.coreSwarmArtifacts.contains(each)) { - each.shouldGather = false; - } - } - } - - for (ArtifactSpec each : this.dependencies) { - if (each.shouldGather) { - gatherDependency(each); - } - } - - for (ArtifactSpec each : this.coreSwarmArtifacts) { - if (each.shouldGather) { - gatherDependency(each); - } - } - } private File createJar(String baseName, Path dir) throws IOException { File out = new File(dir.toFile(), baseName + "-swarm.jar"); @@ -504,34 +204,6 @@ private File createJar(String baseName, Path dir) throws IOException { return out; } - public ArtifactSpec findArtifact(String groupId, String artifactId, String version, String packaging, String classifier) { - for (ArtifactSpec each : this.dependencies) { - if (groupId != null && !groupId.equals(each.groupId())) { - continue; - } - - if (artifactId != null && !artifactId.equals(each.artifactId())) { - continue; - } - - if (version != null && !version.equals(each.version())) { - continue; - } - - if (packaging != null && !packaging.equals(each.type())) { - continue; - } - - if (classifier != null && !classifier.equals(each.classifier())) { - continue; - } - - return each; - } - - return null; - } - public boolean bootstrapJarShadesJBossModules(File artifactFile) throws IOException { JarFile jarFile = new JarFile(artifactFile); Enumeration entries = jarFile.entries(); @@ -564,11 +236,17 @@ public void expandArtifact(File artifactFile) throws IOException { } } - private void addAdditionnalModule() { + private void addAdditionnalModules() { for (String additionnalModule : additionnalModules) { File file = new File(additionnalModule); this.archive.addAsResource(file, "modules"); } } + + private void populateUberJarMavenRepository() throws Exception { + if ( this.bundleDependencies ) { + this.dependencyManager.populateUberJarMavenRepository( this.archive ); + } + } } diff --git a/tools/src/main/java/org/wildfly/swarm/tools/DependencyManager.java b/tools/src/main/java/org/wildfly/swarm/tools/DependencyManager.java new file mode 100644 index 0000000000..9da1bc5fc9 --- /dev/null +++ b/tools/src/main/java/org/wildfly/swarm/tools/DependencyManager.java @@ -0,0 +1,434 @@ +package org.wildfly.swarm.tools; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; + +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.asset.FileAsset; +import org.wildfly.swarm.bootstrap.util.WildFlySwarmApplicationConf; +import org.wildfly.swarm.bootstrap.util.WildFlySwarmBootstrapConf; +import org.wildfly.swarm.bootstrap.util.WildFlySwarmDependenciesConf; + +/** + * @author Bob McWhirter + */ +public class DependencyManager { + + public static final String WILDFLY_SWARM_GROUP_ID = "org.wildfly.swarm"; + + public static final String WILDFLY_SWARM_BOOTSTRAP_ARTIFACT_ID = "wildfly-swarm-bootstrap"; + + public static final String JBOSS_MODULES_GROUP_ID = "org.jboss.modules"; + + public static final String JBOSS_MODULES_ARTIFACT_ID = "jboss-modules"; + + private ArtifactResolvingHelper resolver; + + private final Set dependencies = new HashSet<>(); + + private final Set moduleDependencies = new HashSet<>(); + + private final Set bootstrapDependencies = new HashSet<>(); + + private final Set bootstrapModules = new HashSet<>(); + + private final Set providedGAVs = new HashSet<>(); + + private final Map providedGAVToModuleMappings = new HashMap<>(); + + public DependencyManager() { + } + + Set getDependencies() { + return this.dependencies; + } + + Set getModuleDependencies() { + return this.moduleDependencies; + } + + Set getBootstrapDependencies() { + return this.bootstrapDependencies; + } + + Set getBootstrapModules() { + return this.bootstrapModules; + } + + Set getProvidedGAVs() { + return this.providedGAVs; + } + + Map getProvidedGAVToModuleMappings() { + return this.providedGAVToModuleMappings; + } + + public void setArtifactResolvingHelper(ArtifactResolvingHelper resolver) { + this.resolver = resolver; + } + + public void addDependency(ArtifactSpec dep) { + this.dependencies.add(dep); + } + + protected void analyzeDependencies(boolean resolveTransitive) throws Exception { + if (resolveTransitive) { + Set newDeps = this.resolver.resolveAll(this.dependencies); + this.dependencies.clear(); + this.dependencies.addAll(newDeps); + } else { + for (ArtifactSpec each : this.dependencies) { + resolveArtifact(each); + } + } + + scanBootstrapDependencies(); + analyzeModuleDependencies(); + analyzeProvidedDependencies(); + } + + protected void scanBootstrapDependencies() { + this.dependencies.stream() + .filter(this::isBootstrapDependency) + .map(e -> { + this.bootstrapDependencies.add(e); + return e; + }) + .map(spec -> spec.file) + .forEach(this::scanBootstrapDependency); + } + + protected boolean isBootstrapDependency(ArtifactSpec spec) { + if ( spec.file == null ) { + return false; + } + if (!spec.type().equals("jar")) { + return false; + } + try (JarFile jar = new JarFile(spec.file)) { + ZipEntry entry = jar.getEntry("wildfly-swarm-bootstrap.conf"); + if (entry != null) { + return true; + } + } catch (IOException e) { + } + + if (spec.groupId().equals(WILDFLY_SWARM_GROUP_ID) && spec.artifactId().equals(WILDFLY_SWARM_BOOTSTRAP_ARTIFACT_ID)) { + return true; + } + return false; + } + + protected void scanBootstrapDependency(File file) { + try (JarFile jar = new JarFile(file)) { + ZipEntry entry = jar.getEntry("wildfly-swarm-bootstrap.conf"); + if (entry != null) { + + try (InputStream in = jar.getInputStream(entry)) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line = null; + + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (!line.isEmpty()) { + this.bootstrapModules.add(line); + } + } + } + } + } catch (IOException e) { + } + } + + + protected WildFlySwarmBootstrapConf getWildFlySwarmBootstrapConf() { + WildFlySwarmBootstrapConf bootstrapConf = new WildFlySwarmBootstrapConf(); + + for (ArtifactSpec each : this.bootstrapDependencies) { + bootstrapConf.addEntry(each); + } + + return bootstrapConf; + } + + protected WildFlySwarmApplicationConf getWildFlySwarmApplicationConf(ProjectAsset projectAsset) { + + Set applicationArtifacts = new HashSet<>(); + + for (ArtifactSpec each : this.dependencies) { + if (!this.bootstrapDependencies.contains(each)) { + if (each.type().equals("jar") && each.shouldGather) { + applicationArtifacts.add(each); + } + } + } + + WildFlySwarmApplicationConf appConf = new WildFlySwarmApplicationConf(); + + for (String each : this.bootstrapModules) { + appConf.addEntry(new WildFlySwarmApplicationConf.ModuleEntry(each)); + } + + for (ArtifactSpec each : applicationArtifacts) { + String mapped = this.providedGAVToModuleMappings.get(each.groupId() + ":" + each.artifactId()); + if (mapped != null) { + appConf.addEntry(new WildFlySwarmApplicationConf.ModuleEntry(mapped)); + } else { + if (includeAsBootstrapJar(each)) { + each.shouldGather = true; + appConf.addEntry(new WildFlySwarmApplicationConf.GAVEntry(each)); + } + } + } + + appConf.addEntry(new WildFlySwarmApplicationConf.PathEntry(projectAsset.getName())); + + return appConf; + + } + + protected WildFlySwarmDependenciesConf getWildFlySwarmDependenciesConf() { + WildFlySwarmDependenciesConf depsConf = new WildFlySwarmDependenciesConf(); + + for (ArtifactSpec each : this.dependencies) { + if (providedGAVs.contains(each.groupId() + ":" + each.artifactId())) { + continue; + } + if (each.scope.equals("compile")) { + if (each.type().equals("jar")) { + depsConf.addPrimaryDependency(each); + } else { + depsConf.addExtraDependency(each); + } + } + } + + return depsConf; + } + + public boolean includeAsBootstrapJar(ArtifactSpec dependency) { + + if (dependency.scope.equals("TEST")) { + return false; + } + + if ( isExplodedBootstrap( dependency ) ) { + return false; + } + + return !dependency.scope.equals("PROVIDED"); + } + + public boolean isExplodedBootstrap(ArtifactSpec dependency) { + if (dependency.groupId().equals(JBOSS_MODULES_GROUP_ID) && dependency.artifactId().equals(JBOSS_MODULES_ARTIFACT_ID)) { + return true; + } + if (dependency.groupId().equals(WILDFLY_SWARM_GROUP_ID ) && dependency.artifactId().equals(WILDFLY_SWARM_BOOTSTRAP_ARTIFACT_ID)) { + return true; + } + return false; + } + + public boolean isProvidedDependency(ArtifactSpec dependency) { + String gav = dependency.groupId() + ":" + dependency.artifactId(); + return this.providedGAVs.contains(gav); + } + + protected void analyzeModuleDependencies() { + this.bootstrapDependencies.stream() + .filter(e -> e.type().equals("jar")) + .map(e -> e.file) + .flatMap(DependencyManager::findModuleXmls) + .forEach(this::analyzeModuleDependencies); + + } + + protected static Stream findModuleXmls(File file) { + List xmls = new ArrayList<>(); + try { + JarFile jar = new JarFile(file); + Enumeration entries = jar.entries(); + + while (entries.hasMoreElements()) { + JarEntry each = entries.nextElement(); + String name = each.getName(); + + if (name.startsWith("modules/") && name.endsWith("module.xml")) { + InputStream in = jar.getInputStream(each); + xmls.add(in); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + return xmls.stream(); + } + + protected void analyzeModuleDependencies(InputStream in) { + try { + ModuleAnalyzer analyzer = new ModuleAnalyzer(in); + this.moduleDependencies.addAll(analyzer.getDependencies()); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + protected void analyzeProvidedDependencies() { + this.dependencies.stream() + .filter(e -> e.type().equals("jar")) + .forEach(this::analyzeProvidedDependencies); + } + + protected void analyzeProvidedDependencies(ArtifactSpec spec) { + if ( spec.file == null ) { + return; + } + try (JarFile jar = new JarFile(spec.file)) { + + ZipEntry entry = jar.getEntry("provided-dependencies.txt"); + if (entry != null) { + // add ourselves + providedGAVs.add(spec.groupId() + ":" + spec.artifactId()); + + try (InputStream in = jar.getInputStream(entry)) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line = null; + + // add everything mentioned in the file + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.length() > 0) { + String[] parts = line.split("\\|"); + if (parts.length > 1) { + this.providedGAVToModuleMappings.put(parts[0], parts[1]); + } + providedGAVs.add(parts[0].trim()); + } + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected ArtifactSpec resolveArtifact(ArtifactSpec spec) throws Exception { + if (spec.file == null) { + ArtifactSpec newArtifact = this.resolver.resolve(spec); + + if (newArtifact == null) { + throw new BuildException("Unable to resolve artifact: " + spec); + } + + spec.file = newArtifact.file; + } + + return spec; + } + + public ArtifactSpec findWildFlySwarmBootstrapJar() { + return findArtifact(WILDFLY_SWARM_GROUP_ID, WILDFLY_SWARM_BOOTSTRAP_ARTIFACT_ID, null, "jar", null); + } + + public ArtifactSpec findJBossModulesJar() { + return findArtifact(JBOSS_MODULES_GROUP_ID, JBOSS_MODULES_ARTIFACT_ID, null, "jar", null); + } + + public ArtifactSpec findArtifact(String groupId, String artifactId, String version, String packaging, String classifier) { + for (ArtifactSpec each : this.dependencies) { + if (groupId != null && !groupId.equals(each.groupId())) { + continue; + } + + if (artifactId != null && !artifactId.equals(each.artifactId())) { + continue; + } + + if (version != null && !version.equals(each.version())) { + continue; + } + + if (packaging != null && !packaging.equals(each.type())) { + continue; + } + + if (classifier != null && !classifier.equals(each.classifier())) { + continue; + } + + return each; + } + + return null; + } + + public void populateUberJarMavenRepository(Archive archive) throws Exception { + for (ArtifactSpec dependency : this.dependencies) { + if ( ! this.bootstrapDependencies.contains(dependency) && ! this.moduleDependencies.contains(dependency) ) { + dependency.shouldGather = false; + } + if ( includeAsBootstrapJar( dependency) ) { + dependency.shouldGather = true; + } + if ( isExplodedBootstrap( dependency ) ) { + dependency.shouldGather = false; + } + if ( isProvidedDependency( dependency ) ) { + dependency.shouldGather = false; + } + + if ( dependency.shouldGather ) { + addArtifactToArchiveMavenRepository( archive, dependency ); + } + } + + for (ArtifactSpec dependency : this.moduleDependencies) { + addArtifactToArchiveMavenRepository( archive, dependency ); + } + + for ( ArtifactSpec dependency : this.bootstrapDependencies ) { + if ( ! isExplodedBootstrap(dependency) ) { + addArtifactToArchiveMavenRepository(archive, dependency); + } + } + } + + public void addArtifactToArchiveMavenRepository(Archive archive, ArtifactSpec artifact) throws Exception { + + if (artifact.gathered) { + return; + } + artifact = resolveArtifact(artifact); + + StringBuilder artifactPath = new StringBuilder("m2repo/"); + artifactPath.append(artifact.repoPath(true)); + + archive.add(new FileAsset(artifact.file), artifactPath.toString()); + + artifact.gathered = true; + + } + + +} diff --git a/tools/src/test/java/org/wildfly/swarm/tools/DependencyManagerTest.java b/tools/src/test/java/org/wildfly/swarm/tools/DependencyManagerTest.java new file mode 100644 index 0000000000..e0af51813e --- /dev/null +++ b/tools/src/test/java/org/wildfly/swarm/tools/DependencyManagerTest.java @@ -0,0 +1,278 @@ +package org.wildfly.swarm.tools; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionService; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ArchivePath; +import org.jboss.shrinkwrap.api.Node; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.fest.assertions.Assertions.assertThat; + +/** + * @author Bob McWhirter + */ +public class DependencyManagerTest { + + private ArtifactSpec BOOTSTRAP_JAR; + + private ArtifactSpec BOOTSTRAP_EMPTY_A; + + private ArtifactSpec BOOTSTRAP_EMPTY_B; + + private ArtifactSpec BOOTSTRAP_CONF; + + private ArtifactSpec MODULES_A; + + private ArtifactSpec PROVIDED_A; + + private ArtifactSpec COM_SUN_MAIL; + + private ArtifactSpec CXF; + + private ArtifactSpec WS_INTEGRATION; + + private MockArtifactResolver resolver; + + private DependencyManager manager; + + @Before + public void setUp() throws Exception { + BOOTSTRAP_JAR = ArtifactSpec.fromMscGav("org.wildfly.swarm:wildfly-swarm-bootstrap:1.0"); + + BOOTSTRAP_EMPTY_A = ArtifactSpec.fromMscGav("test:bootstrap-empty-A:1.0"); + + BOOTSTRAP_EMPTY_B = ArtifactSpec.fromMscGav("test:bootstrap-empty-B:1.0"); + + BOOTSTRAP_CONF = ArtifactSpec.fromMscGav("test:bootstrap-conf:1.0"); + + MODULES_A = ArtifactSpec.fromMscGav("test:with-modules-A:1.0"); + + PROVIDED_A = ArtifactSpec.fromMscGav("test:provided-A:1.0"); + + COM_SUN_MAIL = ArtifactSpec.fromMscGav("com.sun.mail:javax.mail:1.0"); + + CXF = ArtifactSpec.fromMscGav("org.jboss.ws.cxf:jbossws-cxf-resources:5.1.0.Final:wildfly1000"); + + WS_INTEGRATION = ArtifactSpec.fromMscGav("org.wildfly:wildfly-webservices-server-integration:10.0.0.CR4"); + + resolver = new MockArtifactResolver(); + + resolver.add(BOOTSTRAP_JAR, (archive) -> { + archive.add(EmptyAsset.INSTANCE, "nothing"); + }); + + resolver.add("test:no-module-A:1.0", (archive) -> { + archive.add(EmptyAsset.INSTANCE, "nothing"); + }); + + resolver.add("test:no-module-B:1.0", (archive) -> { + archive.add(EmptyAsset.INSTANCE, "nothing"); + }); + + resolver.add(BOOTSTRAP_EMPTY_A, (archive) -> { + archive.add(EmptyAsset.INSTANCE, "wildfly-swarm-bootstrap.conf"); + }); + + resolver.add(BOOTSTRAP_EMPTY_B, (archive) -> { + archive.add(EmptyAsset.INSTANCE, "wildfly-swarm-bootstrap.conf"); + }); + + resolver.add(BOOTSTRAP_CONF, (archive) -> { + archive.add(new StringAsset( + "com.module1\n" + + "com.module2\n" + ), "wildfly-swarm-bootstrap.conf"); + }); + + resolver.add(MODULES_A, (archive) -> { + archive.add(EmptyAsset.INSTANCE, "wildfly-swarm-bootstrap.conf"); + archive.add(new ClassLoaderAsset("module.xml"), "modules/org/jboss/as/webservices/main/module.xml"); + }); + + resolver.add(PROVIDED_A, (archive) -> { + archive.add(EmptyAsset.INSTANCE, "wildfly-swarm-bootstrap.conf"); + archive.add(new StringAsset( + "com.sun.mail:javax.mail\n" + + "org.keycloak:keycloak-core|org.keycloak.keycloak-core-module" + ), "provided-dependencies.txt"); + + }); + + resolver.add(COM_SUN_MAIL, (archive) -> { + archive.add(EmptyAsset.INSTANCE, "nothing"); + }); + + resolver.add(CXF, (archive) -> { + archive.add(EmptyAsset.INSTANCE, "nothing"); + }); + + resolver.add(WS_INTEGRATION, (archive) -> { + archive.add(EmptyAsset.INSTANCE, "nothing"); + }); + + this.manager = new DependencyManager(); + this.manager.setArtifactResolvingHelper(resolver); + } + + @After + public void tearDownManager() { + this.manager = null; + } + + @Test + public void analyzeDependenciesZero() throws Exception { + manager.analyzeDependencies(false); + assertThat(manager.getDependencies()).isEmpty(); + } + + @Test + public void analyzeDependenciesNoModules() throws Exception { + manager.addDependency(ArtifactSpec.fromMscGav("test:no-module-A:1.0")); + manager.analyzeDependencies(false); + assertThat(manager.getDependencies()).hasSize(1); + assertThat(manager.getBootstrapDependencies()).isEmpty(); + } + + @Test + public void analyzeDependenciesWithBootstrapJar() throws Exception { + manager.addDependency(ArtifactSpec.fromMscGav("test:no-module-A:1.0")); + manager.addDependency(BOOTSTRAP_JAR); + manager.analyzeDependencies(false); + assertThat(manager.getDependencies()).hasSize(2); + assertThat(manager.getBootstrapDependencies()).hasSize(1); + assertThat(manager.getBootstrapDependencies()).contains(BOOTSTRAP_JAR); + assertThat(manager.getBootstrapModules()).isEmpty(); + } + + @Test + public void analyzeDependenciesWithBootstrapJarAndBootstrapConf() throws Exception { + manager.addDependency(BOOTSTRAP_JAR); + manager.addDependency(ArtifactSpec.fromMscGav("test:no-module-A:1.0")); + manager.addDependency(BOOTSTRAP_EMPTY_A); + manager.addDependency(BOOTSTRAP_EMPTY_B); + manager.analyzeDependencies(false); + assertThat(manager.getDependencies()).hasSize(4); + assertThat(manager.getBootstrapDependencies()).hasSize(3); + assertThat(manager.getBootstrapDependencies()).contains(BOOTSTRAP_JAR); + assertThat(manager.getBootstrapDependencies()).contains(BOOTSTRAP_EMPTY_A); + assertThat(manager.getBootstrapDependencies()).contains(BOOTSTRAP_EMPTY_B); + assertThat(manager.getBootstrapModules()).isEmpty(); + } + + @Test + public void analyzeDependenciesWithBootstrapConfContents() throws Exception { + manager.addDependency(BOOTSTRAP_JAR); + manager.addDependency(BOOTSTRAP_CONF); + manager.analyzeDependencies(false); + assertThat(manager.getDependencies()).hasSize(2); + assertThat(manager.getBootstrapDependencies()).hasSize(2); + assertThat(manager.getBootstrapDependencies()).contains(BOOTSTRAP_JAR); + assertThat(manager.getBootstrapDependencies()).contains(BOOTSTRAP_CONF); + assertThat(manager.getBootstrapModules()).hasSize(2); + assertThat(manager.getBootstrapModules()).contains("com.module1"); + assertThat(manager.getBootstrapModules()).contains("com.module2"); + } + + @Test + public void analyzeDependenciesWithModuleXml() throws Exception { + manager.addDependency(MODULES_A); + manager.analyzeDependencies(false); + assertThat(manager.getDependencies()).hasSize(1); + assertThat(manager.getDependencies()).contains(MODULES_A); + assertThat(manager.getBootstrapDependencies()).hasSize(1); + assertThat(manager.getBootstrapDependencies()).contains(MODULES_A); + + assertThat(manager.getModuleDependencies()).contains(CXF); + assertThat(manager.getModuleDependencies()).contains(WS_INTEGRATION); + } + + @Test + public void populateUberJarMavenRepository() throws Exception { + manager.addDependency(BOOTSTRAP_JAR); + manager.addDependency(BOOTSTRAP_CONF); + manager.addDependency(BOOTSTRAP_EMPTY_A); + manager.addDependency(MODULES_A); + manager.analyzeDependencies(false); + + Archive archive = ShrinkWrap.create(JavaArchive.class); + + manager.populateUberJarMavenRepository(archive); + + Map content = archive.getContent(); + + List jars = content.keySet().stream().map((e) -> e.get()).filter((e) -> e.endsWith(".jar")).collect(Collectors.toList()); + + assertThat(jars).hasSize(5); + assertThat(jars).contains("/m2repo/" + BOOTSTRAP_EMPTY_A.repoPath(true)); + assertThat(jars).contains("/m2repo/" + BOOTSTRAP_CONF.repoPath(true)); + assertThat(jars).contains("/m2repo/" + MODULES_A.repoPath(true)); + assertThat(jars).contains("/m2repo/" + CXF.repoPath(true)); + assertThat(jars).contains("/m2repo/" + WS_INTEGRATION.repoPath(true)); + } + + @Test + public void analyzeDependenciesWithProvided() throws Exception { + manager.addDependency(PROVIDED_A); + manager.analyzeDependencies(false); + + assertThat(manager.getDependencies()).hasSize(1); + assertThat(manager.getProvidedGAVs()).hasSize(3); + assertThat(manager.getProvidedGAVs()).contains(PROVIDED_A.groupId() + ":" + PROVIDED_A.artifactId()); + assertThat(manager.getProvidedGAVs()).contains("com.sun.mail:javax.mail"); + assertThat(manager.getProvidedGAVs()).contains("org.keycloak:keycloak-core"); + + assertThat(manager.getProvidedGAVToModuleMappings()).hasSize(1); + assertThat(manager.getProvidedGAVToModuleMappings().get("org.keycloak:keycloak-core")).isNotNull(); + assertThat(manager.getProvidedGAVToModuleMappings().get("org.keycloak:keycloak-core")).isEqualTo("org.keycloak.keycloak-core-module"); + } + + @Test + public void populateUberJarMavenRepositoryAvoidingProvided() throws Exception { + manager.addDependency(BOOTSTRAP_JAR); + manager.addDependency(BOOTSTRAP_CONF); + manager.addDependency(BOOTSTRAP_EMPTY_A); + manager.addDependency(MODULES_A); + manager.addDependency(PROVIDED_A); + manager.addDependency(COM_SUN_MAIL); + manager.analyzeDependencies(false); + + assertThat(manager.getProvidedGAVs()).contains("com.sun.mail:javax.mail"); + + Archive archive = ShrinkWrap.create(JavaArchive.class); + + manager.populateUberJarMavenRepository(archive); + + Map content = archive.getContent(); + + List jars = content.keySet().stream().map((e) -> e.get()).filter((e) -> e.endsWith(".jar")).collect(Collectors.toList()); + + assertThat(jars).hasSize(6); + assertThat(jars).contains("/m2repo/" + BOOTSTRAP_EMPTY_A.repoPath(true)); + assertThat(jars).contains("/m2repo/" + BOOTSTRAP_CONF.repoPath(true)); + assertThat(jars).contains("/m2repo/" + MODULES_A.repoPath(true)); + assertThat(jars).contains("/m2repo/" + CXF.repoPath(true)); + assertThat(jars).contains("/m2repo/" + WS_INTEGRATION.repoPath(true)); + assertThat(jars).contains("/m2repo/" + PROVIDED_A.repoPath(true)); + } + + @Test + public void analyzeDependenciesUnresolveable() throws Exception { + manager.addDependency(ArtifactSpec.fromMscGav("no:such-thing:1.0")); + manager.analyzeDependencies(false); + + } + +} diff --git a/tools/src/test/java/org/wildfly/swarm/tools/MockArtifactResolver.java b/tools/src/test/java/org/wildfly/swarm/tools/MockArtifactResolver.java new file mode 100644 index 0000000000..994685f442 --- /dev/null +++ b/tools/src/test/java/org/wildfly/swarm/tools/MockArtifactResolver.java @@ -0,0 +1,58 @@ +package org.wildfly.swarm.tools; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.exporter.ZipExporter; +import org.jboss.shrinkwrap.api.spec.JavaArchive; + +/** + * @author Bob McWhirter + */ +public class MockArtifactResolver implements ArtifactResolvingHelper { + + private Map artifacts = new HashMap<>(); + + private Map resolvedArtifacts = new HashMap<>(); + + public void add(String mscGav, Consumer setup) { + add( ArtifactSpec.fromMscGav( mscGav ), setup ); + } + + public void add(ArtifactSpec spec, Consumer setup) { + Archive archive = ShrinkWrap.create(JavaArchive.class); + setup.accept( archive ); + this.artifacts.put( spec, archive ); + } + + @Override + public ArtifactSpec resolve(ArtifactSpec spec) throws Exception { + File resolved = resolvedArtifacts.get( spec ); + + if ( resolved == null ) { + Archive archive = artifacts.get( spec ); + if ( archive != null ) { + resolved = File.createTempFile( spec.artifactId(), ".jar" ); + resolved.delete(); + resolved.deleteOnExit(); + archive.as(ZipExporter.class).exportTo( resolved ); + this.resolvedArtifacts.put( spec, resolved ); + } + } + spec.file = resolved; + return spec; + + } + + @Override + public Set resolveAll(Set specs) throws Exception { + return null; + } +}