1
0
mirror of https://github.com/SpencerPark/IJava.git synced 2025-04-18 20:36:09 +00:00

Add common repositories and support specifying repos in a POM.

This commit is contained in:
SpencerPark 2018-12-02 00:22:54 -05:00
parent ef44f8f900
commit 3171f924d0
5 changed files with 335 additions and 120 deletions
build.gradle
src/main/java/io/github/spencerpark/ijava

@ -71,6 +71,8 @@ dependencies {
shade group: 'io.github.spencerpark', name: 'jupyter-jvm-basekernel', version: '2.2.3'
shade group: 'org.apache.ivy', name: 'ivy', version: '2.5.0-rc1'
//shade group: 'org.apache.maven', name: 'maven-settings-builder', version: '3.6.0'
shade group: 'org.apache.maven', name: 'maven-model-builder', version: '3.6.0'
testCompile group: 'junit', name: 'junit', version: '4.12'
}

@ -24,10 +24,11 @@
package io.github.spencerpark.ijava;
import io.github.spencerpark.ijava.magics.dependencies.CommonRepositories;
import io.github.spencerpark.ijava.magics.dependencies.Maven;
import io.github.spencerpark.ijava.magics.dependencies.MavenToIvy;
import io.github.spencerpark.jupyter.kernel.magic.registry.CellMagic;
import io.github.spencerpark.jupyter.kernel.magic.registry.LineMagic;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
@ -39,6 +40,10 @@ import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser;
import org.apache.ivy.plugins.repository.url.URLResource;
import org.apache.ivy.plugins.resolver.ChainResolver;
import org.apache.ivy.plugins.resolver.DependencyResolver;
import org.apache.ivy.util.DefaultMessageLogger;
import org.apache.ivy.util.Message;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.ModelBuildingException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@ -63,6 +68,31 @@ import java.util.stream.Collectors;
public class MavenResolver {
private static final String DEFAULT_RESOLVER_NAME = "default";
/**
* "master" includes the artifact published by the module.
* "runtime" includes the dependencies required for the module to run and
* extends "compile" which is the dependencies required to compile the module.
*/
private static final String[] DEFAULT_RESOLVE_CONFS = { "master", "runtime" };
/**
* The ivy artifact type corresponding to a binary artifact for a module.
*/
private static final String JAR_TYPE = "jar";
/**
* The ivy artifact type corresponding to a source code artifact for a module. This
* is still usually a ".jar" file but that corresponds to the "ext" not the "type".
*/
private static final String SOURCE_TYPE = "source";
/**
* The ivy artifact type corresponding to a javadoc (HTML) artifact for a module. This
* * is still usually a ".jar" file but that corresponds to the "ext" not the "type".
*/
private static final String JAVADOC_TYPE = "javadoc";
private static final Pattern IVY_MRID_PATTERN = Pattern.compile(
"^(?<organization>[-\\w/._+=]*)#(?<name>[-\\w/._+=]+)(?:#(?<branch>[-\\w/._+=]+))?;(?<revision>[-\\w/._+=,\\[\\]{}():@]+)$"
);
@ -130,23 +160,34 @@ public class MavenResolver {
throw new IllegalArgumentException("Cannot resolve '" + canonical + "' as maven or ivy coordinates.");
}
private Ivy createDefaultIvyInstance() {
Ivy ivy = new Ivy();
ivy.getLoggerEngine().setDefaultLogger(new DefaultMessageLogger(Message.MSG_VERBOSE));
ivy.setSettings(new IvySettings());
ivy.bind();
return ivy;
}
public List<File> resolveMavenDependency(String canonical) throws IOException, ParseException {
DependencyResolver rootResolver = this.searchAllReposResolver();
IvySettings settings = new IvySettings();
Ivy ivy = this.createDefaultIvyInstance();
IvySettings settings = ivy.getSettings();
settings.addResolver(rootResolver);
settings.setDefaultResolver(rootResolver.getName());
Ivy ivy = Ivy.newInstance(settings);
ResolveOptions resolveOptions = new ResolveOptions();
resolveOptions.setTransitive(true);
resolveOptions.setDownload(true);
ModuleRevisionId artifactIdentifier = MavenResolver.parseCanonicalArtifactName(canonical);
DefaultModuleDescriptor containerModule = DefaultModuleDescriptor.newCallerInstance(
new ModuleRevisionId[]{ artifactIdentifier },
artifactIdentifier,
DEFAULT_RESOLVE_CONFS,
true, // Transitive
false // Changing - the resolver will set this based on SNAPSHOT since they are all m2 compatible
);
@ -157,37 +198,49 @@ public class MavenResolver {
throw new RuntimeException("Error resolving '" + canonical + "'. " + resolved.getAllProblemMessages());
return Arrays.stream(resolved.getAllArtifactsReports())
.filter(a -> "jar".equalsIgnoreCase(a.getType()))
.map(ArtifactDownloadReport::getLocalFile)
.collect(Collectors.toList());
}
private File convertPomToIvy(File pomFile) throws IOException, ParseException {
IvySettings settings = new IvySettings();
PomModuleDescriptorParser parser = PomModuleDescriptorParser.getInstance();
URL pomUrl = pomFile.toURI().toURL();
ModuleDescriptor pomModule = parser.parseDescriptor(settings, pomFile.toURI().toURL(), false);
ModuleDescriptor pomModule = parser.parseDescriptor(new IvySettings(), pomFile.toURI().toURL(), false);
File tempIvyFile = File.createTempFile("ijava-ivy-", ".xml").getAbsoluteFile();
tempIvyFile.deleteOnExit();
parser.toIvyFile(pomUrl.openStream(), new URLResource(pomUrl), tempIvyFile, pomModule);
// TODO print to the ivy messaging engine
Files.readAllLines(tempIvyFile.toPath(), Charset.forName("utf8"))
.forEach(System.out::println);
return tempIvyFile;
}
private List<File> resolveFromIvyFile(File ivyFile, List<String> scopes) throws IOException, ParseException {
IvySettings settings = new IvySettings();
// TODO setDefaultCache?
private void addPomReposToIvySettings(IvySettings settings, File pomFile) throws ModelBuildingException {
Model mavenModel = Maven.getInstance().readEffectiveModel(pomFile).getEffectiveModel();
ChainResolver pomRepos = MavenToIvy.createChainForModelRepositories(mavenModel);
pomRepos.setName(DEFAULT_RESOLVER_NAME);
Ivy ivy = Ivy.newInstance(settings);
settings.addResolver(pomRepos);
settings.setDefaultResolver(DEFAULT_RESOLVER_NAME);
}
private List<File> resolveFromIvyFile(Ivy ivy, File ivyFile, List<String> scopes) throws IOException, ParseException {
ResolveOptions resolveOptions = new ResolveOptions();
resolveOptions.setTransitive(true);
resolveOptions.setDownload(true);
resolveOptions.setConfs(!scopes.isEmpty()
? scopes.toArray(new String[0])
: DEFAULT_RESOLVE_CONFS
);
ResolveReport resolved = ivy.resolve(ivyFile);
ResolveReport resolved = ivy.resolve(ivyFile, resolveOptions);
if (resolved.hasError())
// TODO better error...
throw new RuntimeException("Error resolving '" + ivyFile + "'. " + resolved.getAllProblemMessages());
@ -317,7 +370,10 @@ public class MavenResolver {
}
public void addJarsToClasspath(Iterable<String> jars) {
jars.forEach(this.addToClasspath);
jars.forEach(jar -> {
System.out.println("Adding " + jar);
this.addToClasspath.accept(jar);
});
}
@LineMagic(aliases = { "addMavenDependency", "maven" })
@ -370,17 +426,23 @@ public class MavenResolver {
if (args.isEmpty())
throw new IllegalArgumentException("Loading from POM requires at least the path to the POM file");
String pomFile = args.get(0);
String pomPath = args.get(0);
List<String> scopes = args.subList(1, args.size());
File pomFile = new File(pomPath);
try {
File ivyFile = this.convertPomToIvy(new File(pomFile));
File ivyFile = this.convertPomToIvy(pomFile);
Ivy ivy = this.createDefaultIvyInstance();
IvySettings settings = ivy.getSettings();
this.addPomReposToIvySettings(settings, pomFile);
this.addJarsToClasspath(
this.resolveFromIvyFile(ivyFile, scopes).stream()
this.resolveFromIvyFile(ivy, ivyFile, scopes).stream()
.map(File::getAbsolutePath)
::iterator
);
} catch (IOException | ParseException e) {
} catch (IOException | ParseException | ModelBuildingException e) {
throw new RuntimeException(e);
}
}

@ -3,114 +3,12 @@ package io.github.spencerpark.ijava.magics.dependencies;
import org.apache.ivy.plugins.repository.file.FileRepository;
import org.apache.ivy.plugins.resolver.DependencyResolver;
import org.apache.ivy.plugins.resolver.IBiblioResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CommonRepositories {
private static Path getUserHomePath() {
String home = System.getProperty("user.home");
return Paths.get(home).toAbsolutePath();
}
private static final Pattern MAVEN_VAR_PATTERN = Pattern.compile("\\$\\{(?<name>[^}*])}");
private static String replaceMavenVars(String raw) {
StringBuilder replaced = new StringBuilder();
Matcher matcher = MAVEN_VAR_PATTERN.matcher(raw);
while (matcher.find())
matcher.appendReplacement(replaced,
System.getProperty(matcher.group("name"), ""));
matcher.appendTail(replaced);
return replaced.toString();
}
// Thanks gradle!
private static Path getUserMavenHomePath() {
return CommonRepositories.getUserHomePath().resolve(".m2");
}
private static Path getGlobalMavenHomePath() {
String envM2Home = System.getenv("M2_HOME");
return envM2Home != null
? Paths.get(envM2Home).toAbsolutePath() : null;
}
private static Path getUserMavenSettingsPath() {
return CommonRepositories.getUserMavenHomePath().resolve("settings.xml");
}
private static Path getGlobalMavenSettingsPath() {
Path sysHome = CommonRepositories.getGlobalMavenHomePath();
return sysHome != null ? sysHome.resolve("conf").resolve("settings.xml") : null;
}
private static Path getDefaultMavenLocalRepoPath() {
return CommonRepositories.getUserMavenHomePath().resolve("repository");
}
private static Path readConfiguredLocalRepositoryPath(Path settingsXmlPath) throws IOException, SAXException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
// We are configuring the factory, the configuration will be fine...
e.printStackTrace();
return null;
}
try (InputStream in = Files.newInputStream(settingsXmlPath)) {
Document settingsDoc = builder.parse(in);
NodeList settings = settingsDoc.getElementsByTagName("settings");
if (settings.getLength() == 0)
return null;
for (int i = 0; i < settings.getLength(); i++) {
Node setting = settings.item(i);
switch (setting.getNodeName()) {
case "localRepository":
String localRepository = setting.getTextContent();
localRepository = CommonRepositories.replaceMavenVars(localRepository);
return Paths.get(localRepository);
}
}
}
return null;
}
private static Path getMavenLocalRepositoryPath() throws IOException, SAXException {
Path userSettingsXmlPath = CommonRepositories.getUserMavenSettingsPath();
Path path = CommonRepositories.readConfiguredLocalRepositoryPath(userSettingsXmlPath);
if (path == null) {
Path globalSettingsXmlPath = CommonRepositories.getGlobalMavenSettingsPath();
if (globalSettingsXmlPath != null)
path = CommonRepositories.readConfiguredLocalRepositoryPath(globalSettingsXmlPath);
}
return path == null ? CommonRepositories.getDefaultMavenLocalRepoPath() : path;
}
public static DependencyResolver maven(String name, String urlRaw) {
IBiblioResolver resolver = new IBiblioResolver();
resolver.setM2compatible(true);
@ -140,7 +38,7 @@ public class CommonRepositories {
Path localRepoPath;
try {
localRepoPath = CommonRepositories.getMavenLocalRepositoryPath();
localRepoPath = Maven.getInstance().getConfiguredLocalRepositoryPath();
} catch (IOException e) {
throw new RuntimeException("Error reading maven settings. " + e.getLocalizedMessage(), e);
} catch (SAXException e) {

@ -0,0 +1,211 @@
package io.github.spencerpark.ijava.magics.dependencies;
import org.apache.maven.building.StringSource;
import org.apache.maven.model.building.*;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Maven {
private static final Pattern MAVEN_VAR_PATTERN = Pattern.compile("\\$\\{(?<name>[^}*])}");
private static final Maven INSTANCE = new Maven(new Properties(), Collections.emptyMap());
public static Maven getInstance() {
return INSTANCE;
}
// User provider environment overrides.
private final Properties properties;
private final Map<String, String> environment;
public Maven(Properties properties, Map<String, String> environment) {
this.properties = properties;
this.environment = environment;
}
private String getProperty(String name, String def) {
String val = this.environment.get(name);
if (val != null)
return val;
val = System.getProperty(name);
return val != null ? val : def;
}
private String getProperty(String name) {
return this.getProperty(name, null);
}
private String getEnv(String name, String def) {
String val = this.environment.get(name);
if (val != null)
return val;
val = System.getenv(name);
return val != null ? val : def;
}
private String getEnv(String name) {
return this.getEnv(name, null);
}
public Path getUserSystemHomePath() {
String home = this.getProperty("user.home");
return Paths.get(home).toAbsolutePath();
}
private String replaceMavenVars(String raw) {
StringBuilder replaced = new StringBuilder();
Matcher matcher = MAVEN_VAR_PATTERN.matcher(raw);
while (matcher.find())
matcher.appendReplacement(replaced,
System.getProperty(matcher.group("name"), ""));
matcher.appendTail(replaced);
return replaced.toString();
}
// Thanks gradle!
private Path getUserHomePath() {
return this.getUserSystemHomePath().resolve(".m2");
}
private Path getGlobalHomePath() {
String envM2Home = this.getEnv("M2_HOME");
return envM2Home != null
? Paths.get(envM2Home).toAbsolutePath() : null;
}
private Path getUserSettingsPath() {
return this.getUserHomePath().resolve("settings.xml");
}
private Path getGlobalSettingsPath() {
Path sysHome = this.getGlobalHomePath();
return sysHome != null ? sysHome.resolve("conf").resolve("settings.xml") : null;
}
private Path getDefaultLocalRepoPath() {
return this.getUserHomePath().resolve("repository");
}
private Path readConfiguredLocalRepositoryPath(Path settingsXmlPath) throws IOException, SAXException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
// We are configuring the factory, the configuration will be fine...
e.printStackTrace();
return null;
}
try (InputStream in = Files.newInputStream(settingsXmlPath)) {
Document settingsDoc = builder.parse(in);
NodeList settings = settingsDoc.getElementsByTagName("settings");
if (settings.getLength() == 0)
return null;
for (int i = 0; i < settings.getLength(); i++) {
Node setting = settings.item(i);
switch (setting.getNodeName()) {
case "localRepository":
String localRepository = setting.getTextContent();
localRepository = this.replaceMavenVars(localRepository);
return Paths.get(localRepository);
}
}
}
return null;
}
// TODO just use the effective settings
public Path getConfiguredLocalRepositoryPath() throws IOException, SAXException {
Path userSettingsXmlPath = this.getUserSettingsPath();
Path path = this.readConfiguredLocalRepositoryPath(userSettingsXmlPath);
if (path == null) {
Path globalSettingsXmlPath = this.getGlobalSettingsPath();
if (globalSettingsXmlPath != null)
path = this.readConfiguredLocalRepositoryPath(globalSettingsXmlPath);
}
return path == null ? this.getDefaultLocalRepoPath() : path;
}
/*public SettingsBuildingResult readEffectiveSettings() throws SettingsBuildingException {
DefaultSettingsBuilder settingsBuilder = new DefaultSettingsBuilderFactory().newInstance();
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
request.setSystemProperties(System.getProperties());
request.setUserProperties(this.properties);
request.setUserSettingsFile(this.getUserSettingsPath().toFile());
Path globalSettingsPath = this.getGlobalSettingsPath();
request.setGlobalSettingsFile(globalSettingsPath != null ? globalSettingsPath.toFile() : null);
return settingsBuilder.build(request);
}*/
public ModelBuildingResult readEffectiveModel(CharSequence pom) throws ModelBuildingException {
return this.readEffectiveModel(req ->
req.setModelSource((ModelSource) new StringSource(pom))
);
}
public ModelBuildingResult readEffectiveModel(File pom) throws ModelBuildingException {
return this.readEffectiveModel(req ->
req.setPomFile(pom)
);
}
private ModelBuildingResult readEffectiveModel(Function<ModelBuildingRequest, ModelBuildingRequest> configuration) throws ModelBuildingException {
DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
ModelBuildingRequest request = new DefaultModelBuildingRequest();
request.setSystemProperties(System.getProperties());
request.setUserProperties(this.properties);
// Allow force selection of active profile
// request.setActiveProfileIds()
// request.setInactiveProfileIds()
// Better error messages for bad poms
request.setLocationTracking(true);
// Don't run plugins, in most cases this is what we want. I don't know of any
// that would affect the POM.
request.setProcessPlugins(false);
request = configuration.apply(request);
return modelBuilder.build(request);
}
}

@ -0,0 +1,42 @@
package io.github.spencerpark.ijava.magics.dependencies;
import org.apache.ivy.plugins.resolver.ChainResolver;
import org.apache.ivy.plugins.resolver.DependencyResolver;
import org.apache.maven.model.Model;
import org.apache.maven.model.Repository;
import org.apache.maven.model.building.ModelBuildingException;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
public class MavenToIvy {
public static List<DependencyResolver> getRepositoriesFromModel(CharSequence pom) throws ModelBuildingException {
return MavenToIvy.getRepositoriesFromModel(Maven.getInstance().readEffectiveModel(pom).getEffectiveModel());
}
public static List<DependencyResolver> getRepositoriesFromModel(File pom) throws ModelBuildingException {
return MavenToIvy.getRepositoriesFromModel(Maven.getInstance().readEffectiveModel(pom).getEffectiveModel());
}
public static List<DependencyResolver> getRepositoriesFromModel(Model model) {
return model.getRepositories().stream()
.map(MavenToIvy::convertRepository)
.collect(Collectors.toList());
}
public static DependencyResolver convertRepository(Repository repository) {
return CommonRepositories.maven(repository.getId(), repository.getUrl());
}
public static ChainResolver createChainForModelRepositories(Model model) {
ChainResolver resolver = new ChainResolver();
// Maven central is always an implicit repository.
resolver.add(CommonRepositories.mavenCentral());
MavenToIvy.getRepositoriesFromModel(model).forEach(resolver::add);
return resolver;
}
}