Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SHRINKWRAP-261] Maven shortcut API #25

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.shrinkwrap.resolver.impl.maven;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.logging.Logger;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.apache.maven.model.Model;
import org.jboss.shrinkwrap.api.GenericArchive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.importer.ZipImporter;
import org.jboss.shrinkwrap.resolver.api.ResolutionException;
import org.jboss.shrinkwrap.resolver.api.maven.MavenDependency;
import org.jboss.shrinkwrap.resolver.api.maven.MavenResolutionFilter;
import org.jboss.shrinkwrap.resolver.api.maven.filter.StrictFilter;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.artifact.Artifact;
import org.sonatype.aether.artifact.ArtifactTypeRegistry;
import org.sonatype.aether.collection.CollectRequest;
import org.sonatype.aether.collection.DependencyCollectionException;
import org.sonatype.aether.resolution.ArtifactResolutionException;
import org.sonatype.aether.resolution.ArtifactResult;

/**
* Shortcut API for Maven artifact builder which holds and construct dependencies and
* is able to resolve them into ShrinkWrap archives.
*
* @author <a href="mailto:samaxes@gmail.com">Samuel Santos</a>
*/
public class Maven
{
private static final Logger log = Logger.getLogger(Maven.class.getName());

private static final File[] FILE_CAST = new File[0];

/**
* Resolves dependency for dependency builder.
*
* @param coordinates Coordinates specified to a created artifact, specified
* in an implementation-specific format.
* @return An archive of the resolved artifact.
* @throws ResolutionException If artifact coordinates are wrong or if
* version cannot be determined.
*/
public static GenericArchive artifact(String coordinates) throws ResolutionException
{
return new MavenResolver().artifact(coordinates);
}

/**
* Resolves dependencies for dependency builder.
*
* @param coordinates A list of coordinates specified to the created
* artifacts, specified in an implementation-specific format.
* @return An array of archives which contains resolved artifacts.
* @throws ResolutionException If artifact coordinates are wrong or if
* version cannot be determined.
*/
public static Collection<GenericArchive> artifacts(String... coordinates) throws ResolutionException
{
return new MavenResolver().artifacts(coordinates);
}

/**
* Loads remote repositories for a POM file. If repositories are defined in
* the parent of the POM file and there are accessible via local file
* system, they are set as well.
*
* These remote repositories are used to resolve the artifacts during
* dependency resolution.
*
* Additionally, it loads dependencies defined in the POM file model in an
* internal cache, which can be later used to resolve an artifact without
* explicitly specifying its version.
*
* @param path
* A path to the POM file, must not be {@code null} or empty
* @return A dependency builder with remote repositories set according to
* the content of POM file.
*/
public static MavenResolver withPom(String path)
{
Validate.isReadable(path, "Path to the pom.xml file must be defined and accessible");

return new MavenResolver(path);
}

public static class MavenResolver
{
private final MavenRepositorySystem system;

private final RepositorySystemSession session;

private Stack<MavenDependency> dependencies;

private Map<ArtifactAsKey, MavenDependency> pomInternalDependencyManagement;

MavenResolver()
{
this.system = new MavenRepositorySystem(new MavenRepositorySettings());
this.session = system.getSession();
this.dependencies = new Stack<MavenDependency>();
this.pomInternalDependencyManagement = new HashMap<ArtifactAsKey, MavenDependency>();
}

MavenResolver(String path)
{
this();

File pom = new File(path);
Model model = system.loadPom(pom, session);

ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();

// store all dependency information to be able to retrieve versions later
for (org.apache.maven.model.Dependency dependency : model.getDependencies())
{
MavenDependency d = MavenConverter.fromDependency(dependency, stereotypes);
pomInternalDependencyManagement.put(new ArtifactAsKey(d.getCoordinates()), d);
}
}

public GenericArchive artifact(String coordinates) throws ResolutionException
{
Validate.notNullOrEmpty(coordinates, "Artifact coordinates must not be null or empty");

final Collection<GenericArchive> archives = fetchArchives(coordinates);

return (archives == null || archives.isEmpty()) ? null : archives.iterator().next();
}

public Collection<GenericArchive> artifacts(String... coordinates) throws ResolutionException
{
Validate.notNullAndNoNullValues(coordinates, "Artifacts coordinates must not be null or empty");

return fetchArchives(coordinates);
}

// resolves dependencies as files
private Collection<GenericArchive> fetchArchives(String... coordinates) throws ResolutionException
{
for (String coordinate : coordinates)
{
coordinate = MavenConverter.resolveArtifactVersion(pomInternalDependencyManagement, coordinate);
MavenDependency dependency = new MavenDependencyImpl(coordinate);
dependencies.push(dependency);
}

final MavenResolutionFilter filter = new StrictFilter(); // Omit all transitive dependencies
final File[] files = resolveAsFiles(filter);
final Collection<GenericArchive> archives = new ArrayList<GenericArchive>(files.length);

for (final File file : files)
{
final GenericArchive archive = ShrinkWrap.create(ZipImporter.class, file.getName())
.importFrom(convert(file)).as(GenericArchive.class);
archives.add(archive);
}

return archives;
}

// resolves dependencies as files
private File[] resolveAsFiles(MavenResolutionFilter filter) throws ResolutionException
{
Validate.notEmpty(dependencies, "No dependencies were set for resolution");

CollectRequest request = new CollectRequest(MavenConverter.asDependencies(dependencies), null,
system.getRemoteRepositories());

// configure filter
filter.configure(Collections.unmodifiableList(dependencies));

// wrap artifact files to archives
Collection<ArtifactResult> artifacts;
try
{
artifacts = system.resolveDependencies(session, request, filter);
}
catch (DependencyCollectionException e)
{
throw new ResolutionException("Unable to collect dependeny tree for a resolution", e);
}
catch (ArtifactResolutionException e)
{
throw new ResolutionException("Unable to resolve an artifact", e);
}

Collection<File> files = new ArrayList<File>(artifacts.size());
for (ArtifactResult artifact : artifacts)
{
Artifact a = artifact.getArtifact();
// skip all pom artifacts
if ("pom".equals(a.getExtension()))
{
log.info("Removed POM artifact " + a.toString() + " from archive, it's dependencies were fetched.");
continue;
}

files.add(a.getFile());
}

return files.toArray(FILE_CAST);
}

// Converts a file to a ZIP file
private ZipFile convert(File file) throws ResolutionException
{
try
{
return new ZipFile(file);
}
catch (ZipException e)
{
throw new ResolutionException("Unable to treat dependency artifact \"" + file.getAbsolutePath()
+ "\" as a ZIP file", e);
}
catch (IOException e)
{
throw new ResolutionException("Unable to access artifact file at \"" + file.getAbsolutePath() + "\".", e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public class DependenciesUnitTestCase
@BeforeClass
public static void setRemoteRepository()
{
System.setProperty(MavenRepositorySettings.ALT_USER_SETTINGS_XML_LOCATION, "target/settings/profiles/settings.xml");
System.setProperty(MavenRepositorySettings.ALT_USER_SETTINGS_XML_LOCATION,
"target/settings/profiles/settings.xml");
System.setProperty(MavenRepositorySettings.ALT_LOCAL_REPOSITORY_LOCATION, "target/the-other-repository");
}

Expand Down Expand Up @@ -81,7 +82,28 @@ public void testSimpleResolution() throws ResolutionException
DependencyResolvers.use(MavenDependencyResolver.class)
.artifact("org.jboss.shrinkwrap.test:test-deps-c:1.0.0").resolveAs(GenericArchive.class));

DependencyTreeDescription desc = new DependencyTreeDescription(new File("src/test/resources/dependency-trees/test-deps-c.tree"));
DependencyTreeDescription desc = new DependencyTreeDescription(new File(
"src/test/resources/dependency-trees/test-deps-c.tree"));
desc.validateArchive(war).results();

war.as(ZipExporter.class).exportTo(new File("target/" + name + ".war"), true);
}

/**
* Tests a resolution of an artifact from central
*
* @throws ResolutionException
*/
@Test
public void testShortcutSimpleResolution() throws ResolutionException
{
String name = "shortcutSimpleResolution";

WebArchive war = ShrinkWrap.create(WebArchive.class, name + ".war").addAsLibraries(
Maven.artifact("org.jboss.shrinkwrap.test:test-deps-c:1.0.0"));

DependencyTreeDescription desc = new DependencyTreeDescription(new File(
"src/test/resources/dependency-trees/test-deps-c-shortcut.tree"));
desc.validateArchive(war).results();

war.as(ZipExporter.class).exportTo(new File("target/" + name + ".war"), true);
Expand All @@ -102,11 +124,11 @@ public void testSimpleResolutionWithCustomSettings() throws ResolutionException
.configureFrom("target/settings/profiles/settings.xml")
.artifact("org.jboss.shrinkwrap.test:test-deps-c:1.0.0").resolveAs(GenericArchive.class));

DependencyTreeDescription desc = new DependencyTreeDescription(new File("src/test/resources/dependency-trees/test-deps-c.tree"));
DependencyTreeDescription desc = new DependencyTreeDescription(new File(
"src/test/resources/dependency-trees/test-deps-c.tree"));
desc.validateArchive(war).results();

war.as(ZipExporter.class).exportTo(new File("target/" + name + ".war"), true);

}

/**
Expand All @@ -119,12 +141,10 @@ public void testInvalidSettingsPath() throws ResolutionException
{

// this should fail
ShrinkWrap.create(WebArchive.class, "testSimpleResolutionWithCustomSettings.war")
.addAsLibraries(DependencyResolvers.use(MavenDependencyResolver.class)
ShrinkWrap.create(WebArchive.class, "testSimpleResolutionWithCustomSettings.war").addAsLibraries(
DependencyResolvers.use(MavenDependencyResolver.class)
.configureFrom("src/test/invalid/custom-settings.xml")
.artifact("org.jboss.shrinkwrap.test:test-deps-c:1.0.0")
.resolveAs(GenericArchive.class));

.artifact("org.jboss.shrinkwrap.test:test-deps-c:1.0.0").resolveAs(GenericArchive.class));
}

/**
Expand All @@ -142,11 +162,11 @@ public void testMultipleResolution() throws ResolutionException
.artifact("org.jboss.shrinkwrap.test:test-deps-c:1.0.0")
.artifact("org.jboss.shrinkwrap.test:test-deps-g:1.0.0").resolveAs(GenericArchive.class));

DependencyTreeDescription desc = new DependencyTreeDescription(new File("src/test/resources/dependency-trees/test-deps-c+g.tree"));
DependencyTreeDescription desc = new DependencyTreeDescription(new File(
"src/test/resources/dependency-trees/test-deps-c+g.tree"));
desc.validateArchive(war).results();

war.as(ZipExporter.class).exportTo(new File("target/" + name + ".war"), true);

}

/**
Expand All @@ -165,11 +185,31 @@ public void testMultipleResolutionSingleCall() throws ResolutionException
.artifacts("org.jboss.shrinkwrap.test:test-deps-c:1.0.0",
"org.jboss.shrinkwrap.test:test-deps-g:1.0.0").resolveAs(GenericArchive.class));

DependencyTreeDescription desc = new DependencyTreeDescription(new File("src/test/resources/dependency-trees/test-deps-c+g.tree"));
DependencyTreeDescription desc = new DependencyTreeDescription(new File(
"src/test/resources/dependency-trees/test-deps-c+g.tree"));
desc.validateArchive(war).results();

war.as(ZipExporter.class).exportTo(new File("target/" + name + ".war"), true);

}

/**
* Tests a resolution of two artifacts from central using single call
*
* @throws ResolutionException
*/
@Test
public void testShortcutMultipleResolutionSingleCall() throws ResolutionException
{
String name = "shortcutMultipleResolutionSingleCall";

WebArchive war = ShrinkWrap.create(WebArchive.class, name + ".war").addAsLibraries(
Maven.artifacts("org.jboss.shrinkwrap.test:test-deps-c:1.0.0",
"org.jboss.shrinkwrap.test:test-deps-g:1.0.0"));

DependencyTreeDescription desc = new DependencyTreeDescription(new File(
"src/test/resources/dependency-trees/test-deps-c+g-shortcut.tree"));
desc.validateArchive(war).results();

war.as(ZipExporter.class).exportTo(new File("target/" + name + ".war"), true);
}
}
Loading