Skip to content

Commit

Permalink
Add a nebulaRecommenderBom configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
rspieldenner committed Dec 14, 2016
1 parent 119a6a8 commit 677f5aa
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 128 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,8 @@
3.7.0 / 2016-12-13
==================

* Add `nebulaRecommenderBom` configuration as an alternative to the extension method `mavenBom`

3.6.3 / 2016-07-19
==================

Expand Down
59 changes: 33 additions & 26 deletions README.md
Expand Up @@ -46,17 +46,24 @@ Dependency recommenders are the source of versions. If more than one recommende

```groovy
dependencyRecommendations {
mavenBom module: 'netflix:platform:latest.release'
propertiesFile uri: 'http://somewhere/extlib.properties', name: 'myprops'
mavenBom module: 'netflix:platform:latest.release'
propertiesFile uri: 'http://somewhere/extlib.properties', name: 'myprops'
}
dependencies {
compile 'com.google.guava:guava' // no version, version is recommended
compile 'commons-lang:commons-lang:2.6' // I know what I want, don't recommend
compile project.recommend('commmons-logging:commons-logging', 'myprops') // source the recommendation from the provider named myprops'
compile 'com.google.guava:guava' // no version, version is recommended
compile 'commons-lang:commons-lang:2.6' // I know what I want, don't recommend
compile project.recommend('commmons-logging:commons-logging', 'myprops') // source the recommendation from the provider named myprops'
}
```

You can also specify bom lookup via a configuration
```groovy
dependencies {
nebulaRecommenderBom 'test.nebula:bom:1.0.0'
}
```

## 3. Built-in recommendation providers

Several recommendation providers pack with the plugin. The file-based providers all a shared basic configuration that is described separately.
Expand Down Expand Up @@ -91,27 +98,27 @@ dependencies {
}
publishing {
publications {
parent(MavenPublication) {
// the transitive closure of this configuration will be flattened and added to the dependency management section
dependencyManagement.fromConfigurations { configurations.compile }
// alternative syntax when you want to explicitly add a dependency with no transitives
dependencyManagement.withDependencies { 'manual:dep:1' }
// the bom will be generated with dependency coordinates of netflix:module-parent:1
artifactId = 'module-parent'
version = 1
// further customization of the POM is allowed if desired
pom.withXml { asNode().appendNode('description', 'A demonstration of maven POM customization') }
}
}
repositories {
maven {
url "$buildDir/repo" // point this to your destination repository
}
}
publications {
parent(MavenPublication) {
// the transitive closure of this configuration will be flattened and added to the dependency management section
dependencyManagement.fromConfigurations { configurations.compile }
// alternative syntax when you want to explicitly add a dependency with no transitives
dependencyManagement.withDependencies { 'manual:dep:1' }
// the bom will be generated with dependency coordinates of netflix:module-parent:1
artifactId = 'module-parent'
version = 1
// further customization of the POM is allowed if desired
pom.withXml { asNode().appendNode('description', 'A demonstration of maven POM customization') }
}
}
repositories {
maven {
url "$buildDir/repo" // point this to your destination repository
}
}
}
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -38,7 +38,7 @@ repositories {
dependencies {
compile 'org.apache.maven:maven-model-builder:3.0.4'

testCompile('com.netflix.nebula:nebula-test:4.0.0')
testCompile('com.netflix.nebula:nebula-test:4.4.2')
testCompile('com.github.xmlunit:xmlunit:7ed02fb294') {
exclude module: 'hamcrest-core'
}
Expand Down
Expand Up @@ -16,10 +16,12 @@
import java.util.List;

public class DependencyRecommendationsPlugin implements Plugin<Project> {
public static final String NEBULA_RECOMMENDER_BOM = "nebulaRecommenderBom";
private Logger logger = Logging.getLogger(DependencyRecommendationsPlugin.class);

@Override
public void apply(final Project project) {
project.getConfigurations().create(NEBULA_RECOMMENDER_BOM);
project.getExtensions().create("dependencyRecommendations", RecommendationProviderContainer.class, project);
applyRecommendations(project);
enhanceDependenciesWithRecommender(project);
Expand All @@ -45,38 +47,40 @@ private void applyRecommendations(final Project project) {
@Override
public void execute(final Configuration conf) {
final RecommendationStrategyFactory rsFactory = new RecommendationStrategyFactory(project);
conf.getIncoming().beforeResolve(new Action<ResolvableDependencies>() {
@Override
public void execute(ResolvableDependencies resolvableDependencies) {
for (Dependency dependency : resolvableDependencies.getDependencies()) {
applyRecommendationToDependency(rsFactory, dependency, new ArrayList<ProjectDependency>());
if (conf.getState() == Configuration.State.UNRESOLVED) {
conf.getIncoming().beforeResolve(new Action<ResolvableDependencies>() {
@Override
public void execute(ResolvableDependencies resolvableDependencies) {
for (Dependency dependency : resolvableDependencies.getDependencies()) {
applyRecommendationToDependency(rsFactory, dependency, new ArrayList<ProjectDependency>());

// if project dependency, pull all first orders and apply recomendations if missing dependency versions
// dependency.getProjectConfiguration().allDependencies iterate and inspect them as well
}
// if project dependency, pull all first orders and apply recomendations if missing dependency versions
// dependency.getProjectConfiguration().allDependencies iterate and inspect them as well
}

conf.getResolutionStrategy().eachDependency(new Action<DependencyResolveDetails>() {
@Override
public void execute(DependencyResolveDetails details) {
ModuleVersionSelector requested = details.getRequested();
conf.getResolutionStrategy().eachDependency(new Action<DependencyResolveDetails>() {
@Override
public void execute(DependencyResolveDetails details) {
ModuleVersionSelector requested = details.getRequested();

// don't interfere with the way forces trump everything
for (ModuleVersionSelector force : conf.getResolutionStrategy().getForcedModules()) {
if (requested.getGroup().equals(force.getGroup()) && requested.getName().equals(force.getName())) {
return;
// don't interfere with the way forces trump everything
for (ModuleVersionSelector force : conf.getResolutionStrategy().getForcedModules()) {
if (requested.getGroup().equals(force.getGroup()) && requested.getName().equals(force.getName())) {
return;
}
}
}
RecommendationStrategy strategy = rsFactory.getRecommendationStrategy();
if (strategy.canRecommendVersion(requested)) {
String version = getRecommendedVersionRecursive(project, requested);
if (strategy.recommendVersion(details, version)) {
logger.info("Recommending version " + version + " for dependency " + requested.getGroup() + ":" + requested.getName());
RecommendationStrategy strategy = rsFactory.getRecommendationStrategy();
if (strategy.canRecommendVersion(requested)) {
String version = getRecommendedVersionRecursive(project, requested);
if (strategy.recommendVersion(details, version)) {
logger.info("Recommending version " + version + " for dependency " + requested.getGroup() + ":" + requested.getName());
}
}
}
}
});
}
});
});
}
});
}
}
});
}
Expand Down
@@ -0,0 +1,36 @@
/*
* Copyright 2016 Netflix, Inc.
*
* 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 netflix.nebula.dependency.recommender.provider;

import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;

import java.io.File;
import java.util.Set;

public abstract class ClasspathBasedRecommendationProvider extends AbstractRecommendationProvider {
protected Project project;
private Configuration configuration;

ClasspathBasedRecommendationProvider(Project project, String configName) {
this.project = project;
this.configuration = project.getConfigurations().getByName(configName);
}

Set<File> getFilesOnConfiguration() {
return configuration.resolve();
}
}
Expand Up @@ -18,20 +18,18 @@
import org.gradle.api.artifacts.repositories.MavenArtifactRepository;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;

public class MavenBomRecommendationProvider extends FileBasedRecommendationProvider {
public class MavenBomRecommendationProvider extends ClasspathBasedRecommendationProvider {
private Map<String, String> recommendations;

public MavenBomRecommendationProvider(Project project) {
super(project);
public MavenBomRecommendationProvider(Project project, String configName) {
super(project, configName);
}

private class SimpleModelSource implements ModelSource {
Expand All @@ -57,70 +55,64 @@ public String getVersion(String org, String name) throws Exception {
if(recommendations == null) {
recommendations = new HashMap<>();

DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();

request.setModelResolver(new ModelResolver() {
@Override
public ModelSource resolveModel(String groupId, String artifactId, String version) throws UnresolvableModelException {
String relativeUrl = "";
for(String groupIdPart : groupId.split("\\."))
relativeUrl += groupIdPart + "/";
relativeUrl += artifactId + "/" + version + "/" + artifactId + "-" + version + ".pom";
try {
// try to find the parent pom in each maven repository specified in the gradle file
for(ArtifactRepository repo : project.getRepositories()) {
if(!(repo instanceof MavenArtifactRepository))
continue;
URL url = new URL(((MavenArtifactRepository) repo).getUrl().toString() + "/" + relativeUrl);
try {
return new SimpleModelSource(url.openStream());
} catch (IOException e) {
// try the next repo
Set<File> recommendationFiles = getFilesOnConfiguration();
for (File recommendation : recommendationFiles) {
if (!recommendation.getName().endsWith("pom")) {
break;
}

DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();

request.setModelResolver(new ModelResolver() {
@Override
public ModelSource resolveModel(String groupId, String artifactId, String version) throws UnresolvableModelException {
String relativeUrl = "";
for (String groupIdPart : groupId.split("\\."))
relativeUrl += groupIdPart + "/";
relativeUrl += artifactId + "/" + version + "/" + artifactId + "-" + version + ".pom";
try {
// try to find the parent pom in each maven repository specified in the gradle file
for (ArtifactRepository repo : project.getRepositories()) {
if (!(repo instanceof MavenArtifactRepository))
continue;
URL url = new URL(((MavenArtifactRepository) repo).getUrl().toString() + "/" + relativeUrl);
try {
return new SimpleModelSource(url.openStream());
} catch (IOException e) {
// try the next repo
}
}
} catch (MalformedURLException e) {
throw new RuntimeException(e); // should never happen
}
} catch (MalformedURLException e) {
throw new RuntimeException(e); // should never happen
return null;
}
return null;
}

@Override
public void addRepository(Repository repository) throws InvalidRepositoryException {
// do nothing
}
@Override
public void addRepository(Repository repository) throws InvalidRepositoryException {
// do nothing
}

@Override
public ModelResolver newCopy() {
return this; // do nothing
}
});
@Override
public ModelResolver newCopy() {
return this; // do nothing
}
});

request.setModelSource(new SimpleModelSource(getInput()));
request.setModelSource(new SimpleModelSource(new FileInputStream(recommendation)));

DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
modelBuilder.setModelInterpolator(new ProjectPropertiesModelInterpolator(project));
DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
modelBuilder.setModelInterpolator(new ProjectPropertiesModelInterpolator(project));

ModelBuildingResult result = modelBuilder.build(request);
for (Dependency d : result.getEffectiveModel().getDependencyManagement().getDependencies()) {
recommendations.put(d.getGroupId() + ":" + d.getArtifactId(), d.getVersion());
ModelBuildingResult result = modelBuilder.build(request);
for (Dependency d : result.getEffectiveModel().getDependencyManagement().getDependencies()) {
recommendations.put(d.getGroupId() + ":" + d.getArtifactId(), d.getVersion());
}
}
}
return recommendations.get(org + ":" + name);
}

@SuppressWarnings("unchecked")
@Override
public InputStreamProvider setModule(Object dependencyNotation) {
if(dependencyNotation == null)
throw new IllegalArgumentException("Module may not be null");

if(Map.class.isAssignableFrom(dependencyNotation.getClass()))
((Map) dependencyNotation).put("ext", "pom");
else if(!dependencyNotation.toString().endsWith("@pom"))
dependencyNotation = dependencyNotation.toString() + "@pom";
return super.setModule(dependencyNotation);
}

private static class ProjectPropertiesModelInterpolator extends StringSearchModelInterpolator {
private final Project project;

Expand Down

0 comments on commit 677f5aa

Please sign in to comment.