Compare commits

...

19 Commits

Author SHA1 Message Date
Spencer Park
a03ad7712a
Merge pull request #82 from frankfliu/excutable
Make gradlew executable.
2019-12-08 14:22:20 -08:00
Frank Liu
554c9a0339 Make gradlew executable. 2019-11-29 17:53:34 -08:00
SpencerPark
3975e47f7f Document the %classpath line magic. 2019-05-06 00:28:22 -04:00
SpencerPark
d6695414d8 Update doc references. 2019-05-06 00:25:50 -04:00
SpencerPark
3ea6536b82 Document the kernel runtime functions. 2019-05-06 00:16:23 -04:00
SpencerPark
c594285afa Update display docs to include updateDisplay. 2019-05-05 23:56:33 -04:00
SpencerPark
c99a809dc3 v1.3.0 2019-05-05 23:04:44 -04:00
SpencerPark
a920ff3a27 Remove leftover debug messages. 2019-05-05 21:42:07 -04:00
SpencerPark
fe14b7313e Convert initialization scripts to static imports from the kernel runtime. See #55. 2019-05-05 21:39:37 -04:00
SpencerPark
8e7b6b62b1 Fix #56. 2019-05-05 01:56:54 -04:00
SpencerPark
4ceadf40df Support the %load magic for loading shell scripts and notebooks. See #52 and #54. 2019-05-05 01:44:01 -04:00
SpencerPark
8d030b040c Set language file extension to .jshell. See #46. 2019-05-04 23:49:39 -04:00
SpencerPark
7e0da04cfe Support adding directories to the classpath for including local projects. See #45. 2019-05-04 23:22:29 -04:00
SpencerPark
ed1c8fe1df Fix resolution from maven local. 2019-04-13 14:58:13 -04:00
SpencerPark
b5945808c6 Support specifying repos in the maven magic as well as verbosity level. 2018-12-21 13:11:42 -05:00
SpencerPark
3171f924d0 Add common repositories and support specifying repos in a POM. 2018-12-02 00:22:54 -05:00
SpencerPark
ef44f8f900 Let ivy build the dummy container module. 2018-11-28 15:07:23 -05:00
SpencerPark
4d1ee69e39 Switch to ivy for dependency management magics. 2018-11-27 12:12:42 -05:00
SpencerPark
98386ad554 Update install/configuration instructions based on the new installer plugin. 2018-11-25 23:06:16 -05:00
21 changed files with 1155 additions and 486 deletions

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ plugins {
id 'java'
id 'maven-publish'
id('com.github.hierynomus.license') version '0.14.0'
id('io.github.spencerpark.jupyter-kernel-installer') version '2.0.0'
id('io.github.spencerpark.jupyter-kernel-installer') version '2.1.0'
id('com.github.jk1.dependency-license-report')
}
@ -12,7 +12,7 @@ import com.github.jk1.license.filter.*
import io.github.spencerpark.gradle.*
group = 'io.github.spencerpark'
version = '1.2.0'
version = '1.3.0'
wrapper {
gradleVersion = '4.8.1'
@ -68,13 +68,11 @@ repositories {
}
dependencies {
shade group: 'io.github.spencerpark', name: 'jupyter-jvm-basekernel', version: '2.2.3'
shade group: 'io.github.spencerpark', name: 'jupyter-jvm-basekernel', version: '2.3.0'
shade group: 'org.jboss.shrinkwrap.resolver', name: 'shrinkwrap-resolver-impl-maven', version: '3.1.3'
// Transitive shrinkwrap but repeated to be explicit
shade group: 'org.slf4j', name: 'slf4j-api', version: '1.7.22'
shade group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.22'
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'
}
@ -140,7 +138,6 @@ jupyter {
}
installKernel {
//pythonExecutable = 'python'
kernelInstallPath = commandLineSpecifiedPath(userInstallPath)
}

View File

@ -4,21 +4,21 @@ One of the many great things about the Jupyter front ends is the support for [`d
## Notebook functions
IJava injects 2 functions into the user space for displaying data: `display` and `render`. Most use cases should prefer the former but there is a necessary case for `render` that is outline below. Both are defined in [ijava-display-init.jshell](/src/main/resources/ijava-display-init.jshell).
IJava injects 2 functions into the user space for displaying data: `display` and `render`. Most use cases should prefer the former but there is a necessary case for `render` that is outline below. In addition the `updateDisplay` function can be used to update a previously displayed object. All are defined in the runtime [Display](/src/main/java/io/github/spencerpark/ijava/runtime/Display.java) class.
All display/render functions include a `text/plain` representation in their output. By default this is the `String.valueOf(Object)` value but it can be overridden.
### `display(Object o)`
### `String display(Object o)`
Display an object as it's **preferred** types. If you don't want a specific type it is best to let the object decide how it is best represented.
The object is rendered and published on the display stream.
The object is rendered and published on the display stream. An id is returned which can be used to `updateDisplay` if desired.
### `display(Object o, String... as)`
### `String display(Object o, String... as)`
Display an object as the **requested** types. In this case the object attempts to be rendered as the desired mime types given in `as`. No promises though, if a type is unsupported it will simply not appear in the output.
The object is rendered and published on the display stream.
The object is rendered and published on the display stream. An id is returned which can be used to `updateDisplay` if desired.
This is useful when a type has many potential representations but not all are preferred. For example a `CharSequence` has many representations but only the `text/plain` is preferred. To display it as executable javascript we can use the following:
@ -26,13 +26,20 @@ This is useful when a type has many potential representations but not all are pr
display("alert('Hello from IJava!');", "application/javascript");
```
Since there is the potential that some front ends don't support a given format many can be given and the front end chooses the best.
Since there is the potential that some front ends don't support a given format many can be given and the front end chooses the best. For example, to display as html and markdown:
### `render(Object o)`
```java
display("<b>Bold</b>", "text/html", "text/markdown");
```
This will trigger a display message with values for `text/html`, `text/markdown`, and the implicit `text/plain`.
### `DisplayData render(Object o)`
Renders an object as it's **preferred** types and returns it's rendered format. Similar to `display(Object o)` but without publishing the result.
### `render(Object o, String... as)`
### `DisplayData render(Object o, String... as)`
Renders an object as the **requested** types and returns it's rendered format. Similar to `display(Object o, String... as)` but without publishing the result.
@ -46,4 +53,19 @@ render(md, "text/markdown")
This will result in the `Out[_]` result to be the pretty `text/markdown` representation rather than the boring `text/plain` representation.
### `void updateDisplay(String id, Object o)`
Renders an object as it's **preferred** types and updates an existing display with the given id to contain the new rendered object. Similar to `display(Object o)` but updates an existing displayed object instead of appending a new one.
### `void updateDisplay(String id, Object o, String... as)`
Renders an object as it's **requested** types and updates an existing display with the given id to contain the new rendered object. Similar to `display(Object o, String... as)` but updates an existing displayed object instead of appending a new one.
```java
String id = display("<b>Countdown:</b> 3", "text/html");
for (int i = 3; i >= 0; i--) {
updateDisplay(id, "<b>Countdown:</b> " + i, "text/html");
Thread.sleep(1000L);
}
render("<b>Liftoff!</b>", "text/html")
```

22
docs/kernel.md Normal file
View File

@ -0,0 +1,22 @@
# Kernel
All code running in IJava flows through the kernel. This makes it the place to register magics, add things to the classpath, and perform many jupyter related operations.
## Notebook functions
IJava injects a function for getting the active kernel instance and additional helpers for making use of the kernel at runtime. These are defined in the runtime [Kernel](/src/main/java/io/github/spencerpark/ijava/runtime/Kernel.java) class.
### `JavaKernel getKernelInstance()`
Get a reference to the current kernel. It may return null if called outside of a kernel context but should be considered `@NonNull` when inside a notebook or similar. The kernel api has lots of goodies, look at the [JavaKernel](/src/main/java/io/github/spencerpark/ijava/runtime/Kernel.java) class for more information. Specifically there is access to adding to the classpath, getting the magics registry and maven resolver, and access to eval.
### `Object eval(String expr) throws Exception`
The `eval` function provides full access to the code evaluation mechanism of the kernel. It evaluates the code in the _same_ scope as the kernel and **returns an object**. This object is an object that lives in the kernel!
The given expression can be anything you would write in a cell, including magics.
```java
(int) eval("1 + 2") + 3
```

View File

@ -44,7 +44,18 @@ Add jars to the notebook classpath.
###### Line magic
* **arguments**:
* _varargs_ list of simple glob paths to jars on the local file system
* _varargs_ list of simple glob paths to jars on the local file system. If a glob matches a directory all files in that directory will be added.
### classpath
Add entries to the notebook classpath.
###### Line magic
* **arguments**:
* _varargs_ list of simple glob paths to entries on the local file system. This includes directories or jars.

0
gradlew vendored Normal file → Executable file
View File

View File

@ -46,9 +46,6 @@ public class IJava {
public static final String STARTUP_SCRIPT_KEY = "IJAVA_STARTUP_SCRIPT";
public static final String DEFAULT_SHELL_INIT_RESOURCE_PATH = "ijava-jshell-init.jshell";
public static final String MAGICS_INIT_RESOURCE_PATH = "ijava-magics-init.jshell";
public static final String DISPLAY_INIT_RESOURCE_PATH = "ijava-display-init.jshell";
public static final String EVAL_INIT_RESOURCE_PATH = "ijava-eval-init.jshell";
public static final String VERSION;

View File

@ -24,24 +24,24 @@
package io.github.spencerpark.ijava;
import io.github.spencerpark.ijava.execution.*;
import io.github.spencerpark.ijava.magics.ClasspathMagics;
import io.github.spencerpark.ijava.magics.MavenResolver;
import io.github.spencerpark.jupyter.kernel.BaseKernel;
import io.github.spencerpark.jupyter.kernel.LanguageInfo;
import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
import io.github.spencerpark.jupyter.kernel.display.DisplayData;
import io.github.spencerpark.jupyter.kernel.magic.registry.Magics;
import io.github.spencerpark.jupyter.kernel.magic.common.Load;
import io.github.spencerpark.jupyter.kernel.util.CharPredicate;
import io.github.spencerpark.jupyter.kernel.util.GlobFinder;
import io.github.spencerpark.jupyter.kernel.util.StringStyler;
import io.github.spencerpark.jupyter.kernel.util.TextColor;
import io.github.spencerpark.jupyter.messages.Header;
import jdk.jshell.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class JavaKernel extends BaseKernel {
public static String completeCodeSignifier() {
@ -81,9 +81,6 @@ public class JavaKernel extends BaseKernel {
.addClasspathFromString(System.getenv(IJava.CLASSPATH_KEY))
.compilerOptsFromString(System.getenv(IJava.COMPILER_OPTS_KEY))
.startupScript(IJava.resource(IJava.DEFAULT_SHELL_INIT_RESOURCE_PATH))
.startupScript(IJava.resource(IJava.MAGICS_INIT_RESOURCE_PATH))
.startupScript(IJava.resource(IJava.DISPLAY_INIT_RESOURCE_PATH))
.startupScript(IJava.resource(IJava.EVAL_INIT_RESOURCE_PATH))
.startupScriptFiles(System.getenv(IJava.STARTUP_SCRIPTS_KEY))
.startupScript(System.getenv(IJava.STARTUP_SCRIPT_KEY))
.timeoutFromString(System.getenv(IJava.TIMEOUT_DURATION_KEY))
@ -96,26 +93,13 @@ public class JavaKernel extends BaseKernel {
this.magicsTransformer = new MagicsSourceTransformer();
this.magics = new Magics();
this.magics.registerMagics(this.mavenResolver);
this.magics.registerLineMagic("jars", args -> {
List<String> jars = args.stream()
.map(GlobFinder::new)
.flatMap(g -> {
try {
return StreamSupport.stream(g.computeMatchingPaths().spliterator(), false);
} catch (IOException e) {
throw new RuntimeException("Exception resolving jar glob", e);
}
})
.map(p -> p.toAbsolutePath().toString())
.collect(Collectors.toList());
jars.forEach(this::addToClasspath);
return jars;
});
this.magics.registerMagics(new ClasspathMagics(this::addToClasspath));
this.magics.registerMagics(new Load(List.of(".jsh", ".jshell", ".java", ".ijava"), this::eval));
this.languageInfo = new LanguageInfo.Builder("Java")
.version(Runtime.version().toString())
.mimetype("text/x-java-source")
.fileExtension(".java")
.fileExtension(".jshell")
.pygments("java")
.codemirror("java")
.build();
@ -367,7 +351,7 @@ public class JavaKernel extends BaseKernel {
.distinct()
.collect(Collectors.toList());
return new ReplacementOptions(options, replaceStart[0], at + 1);
return new ReplacementOptions(options, replaceStart[0], at);
}
@Override

View File

@ -1,270 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Spencer Park
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.spencerpark.ijava;
import io.github.spencerpark.jupyter.kernel.magic.registry.CellMagic;
import io.github.spencerpark.jupyter.kernel.magic.registry.LineMagic;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem;
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage;
import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenRemoteRepositories;
import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenRemoteRepository;
import org.jboss.shrinkwrap.resolver.api.maven.strategy.TransitiveStrategy;
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 javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MavenResolver {
private final Consumer<String> addToClasspath;
private final List<MavenRemoteRepository> repos;
public MavenResolver(Consumer<String> addToClasspath) {
this.addToClasspath = addToClasspath;
this.repos = new LinkedList<>();
}
public void addRemoteRepo(String name, String url) {
this.repos.add(MavenRemoteRepositories.createRemoteRepository(name, url, "default"));
}
public void addRemoteRepo(String name, URL url) {
this.repos.add(MavenRemoteRepositories.createRemoteRepository(name, url, "default"));
}
public List<File> resolveMavenDependency(String canonical) {
ConfigurableMavenResolverSystem resolver = Maven.configureResolver()
.withClassPathResolution(true)
.withMavenCentralRepo(true);
this.repos.forEach(resolver::withRemoteRepo);
return resolver.resolve(canonical)
.using(TransitiveStrategy.INSTANCE)
.asList(File.class);
}
private String solidifyPartialPOM(String rawIn) throws ParserConfigurationException, IOException, SAXException, TransformerException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
// Wrap in a dummy tag to allow fragments
InputStream inStream = new SequenceInputStream(Collections.enumeration(Arrays.asList(
new ByteArrayInputStream("<ijava>".getBytes(Charset.forName("utf-8"))),
new ByteArrayInputStream(rawIn.getBytes(Charset.forName("utf-8"))),
new ByteArrayInputStream("</ijava>".getBytes(Charset.forName("utf-8")))
)));
Document doc = builder.parse(inStream);
NodeList rootChildren = doc.getDocumentElement().getChildNodes();
// If input was a single "project" tag then we don't touch it. It is assumed
// to be complete.
if (rootChildren.getLength() == 1 && "project".equalsIgnoreCase(rootChildren.item(0).getNodeName()))
return this.writeDOM(new DOMSource(rootChildren.item(0)));
// Put the pieces together and fill in the blanks.
Document fixed = builder.newDocument();
Node project = fixed.appendChild(fixed.createElement("project"));
Node dependencies = project.appendChild(fixed.createElement("dependencies"));
Node repositories = project.appendChild(fixed.createElement("repositories"));
boolean setModelVersion = false;
boolean setGroupId = false;
boolean setArtifactId = false;
boolean setVersion = false;
for (int i = 0; i < rootChildren.getLength(); i++) {
Node child = rootChildren.item(i);
switch (child.getNodeName()) {
case "modelVersion":
setModelVersion = true;
this.appendChildInNewDoc(child, fixed, project);
break;
case "groupId":
setGroupId = true;
this.appendChildInNewDoc(child, fixed, project);
break;
case "artifactId":
setArtifactId = true;
this.appendChildInNewDoc(child, fixed, project);
break;
case "version":
setVersion = true;
this.appendChildInNewDoc(child, fixed, project);
break;
case "dependency":
this.appendChildInNewDoc(child, fixed, dependencies);
break;
case "repository":
this.appendChildInNewDoc(child, fixed, repositories);
break;
case "dependencies":
// Add all dependencies to the collecting tag
NodeList dependencyChildren = child.getChildNodes();
for (int j = 0; j < dependencyChildren.getLength(); j++)
this.appendChildInNewDoc(dependencyChildren.item(j), fixed, dependencies);
break;
case "repositories":
// Add all repositories to the collecting tag
NodeList repositoryChildren = child.getChildNodes();
for (int j = 0; j < repositoryChildren.getLength(); j++)
this.appendChildInNewDoc(repositoryChildren.item(j), fixed, repositories);
break;
default:
this.appendChildInNewDoc(child, fixed, project);
break;
}
}
if (!setModelVersion) {
Node modelVersion = project.appendChild(fixed.createElement("modelVersion"));
modelVersion.setTextContent("4.0.0");
}
if (!setGroupId) {
Node groupId = project.appendChild(fixed.createElement("groupId"));
groupId.setTextContent("ijava.notebook");
}
if (!setArtifactId) {
Node artifactId = project.appendChild(fixed.createElement("artifactId"));
artifactId.setTextContent("cell");
}
if (!setVersion) {
Node version = project.appendChild(fixed.createElement("version"));
version.setTextContent("1");
}
return this.writeDOM(new DOMSource(fixed));
}
private void appendChildInNewDoc(Node oldNode, Document doc, Node newParent) {
Node newNode = oldNode.cloneNode(true);
doc.adoptNode(newNode);
newParent.appendChild(newNode);
}
private String writeDOM(Source src) throws TransformerException, UnsupportedEncodingException {
Transformer idTransformer = TransformerFactory.newInstance().newTransformer();
idTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
ByteArrayOutputStream out = new ByteArrayOutputStream();
Result dest = new StreamResult(out);
idTransformer.transform(src, dest);
return out.toString("utf-8");
}
public void addJarsToClasspath(Iterable<String> jars) {
jars.forEach(this.addToClasspath);
}
@LineMagic(aliases = { "addMavenDependency", "maven" })
public void addMavenDependencies(List<String> args) {
for (String arg : args) {
this.addJarsToClasspath(
this.resolveMavenDependency(arg).stream()
.map(File::getAbsolutePath)
::iterator
);
}
}
@LineMagic(aliases = { "mavenRepo" })
public void addMavenRepo(List<String> args) {
if (args.size() != 2)
throw new IllegalArgumentException("Expected 2 arguments: repository id and url. Got: " + args);
String id = args.get(0);
String url = args.get(1);
this.addRemoteRepo(id, url);
}
@CellMagic
public void loadFromPOM(List<String> args, String body) throws Exception {
try {
Path tempPom = Files.createTempFile("ijava-maven-", ".pom");
String rawPom = this.solidifyPartialPOM(body);
Files.write(tempPom, rawPom.getBytes(Charset.forName("utf-8")));
List<String> loadArgs = new ArrayList<>(args.size() + 1);
loadArgs.add(tempPom.toAbsolutePath().toString());
loadArgs.addAll(args);
this.loadFromPOM(loadArgs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@LineMagic
public void loadFromPOM(List<String> args) {
if (args.isEmpty())
throw new IllegalArgumentException("Loading from POM requires at least the path to the POM file");
String pomFile = args.get(0);
List<String> scopes = args.subList(1, args.size());
PomEquippedResolveStage stage = Maven.resolver().loadPomFromFile(pomFile);
if (scopes.isEmpty())
stage = stage.importCompileAndRuntimeDependencies();
else
stage = stage.importDependencies(scopes.stream().map(ScopeType::fromScopeType).toArray(ScopeType[]::new));
this.addJarsToClasspath(
stage.resolve()
.withTransitivity()
.asList(File.class).stream()
.map(File::getAbsolutePath)
::iterator
);
}
}

View File

@ -0,0 +1,56 @@
package io.github.spencerpark.ijava.magics;
import io.github.spencerpark.jupyter.kernel.magic.registry.LineMagic;
import io.github.spencerpark.jupyter.kernel.util.GlobFinder;
import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class ClasspathMagics {
private final Consumer<String> addToClasspath;
public ClasspathMagics(Consumer<String> addToClasspath) {
this.addToClasspath = addToClasspath;
}
@LineMagic
public List<String> jars(List<String> args) {
List<String> jars = args.stream()
.map(GlobFinder::new)
.flatMap(g -> {
try {
return StreamSupport.stream(g.computeMatchingFiles().spliterator(), false);
} catch (IOException e) {
throw new RuntimeException("Exception resolving jar glob", e);
}
})
.map(p -> p.toAbsolutePath().toString())
.collect(Collectors.toList());
jars.forEach(this.addToClasspath);
return jars;
}
@LineMagic
public List<String> classpath(List<String> args) {
List<String> paths = args.stream()
.map(GlobFinder::new)
.flatMap(g -> {
try {
return StreamSupport.stream(g.computeMatchingPaths().spliterator(), false);
} catch (IOException e) {
throw new RuntimeException("Exception resolving jar glob", e);
}
})
.map(p -> p.toAbsolutePath().toString())
.collect(Collectors.toList());
paths.forEach(this.addToClasspath);
return paths;
}
}

View File

@ -0,0 +1,524 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Spencer Park
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.spencerpark.ijava.magics;
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 io.github.spencerpark.jupyter.kernel.magic.registry.MagicsArgs;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.report.ArtifactDownloadReport;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.resolve.ResolveOptions;
import org.apache.ivy.core.settings.IvySettings;
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.ivy.util.MessageLogger;
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;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.text.ParseException;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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/._+=,\\[\\]{}():@]+)$"
);
private static final Pattern MAVEN_MRID_PATTERN = Pattern.compile(
"^(?<group>[^:\\s]+):(?<artifact>[^:\\s]+)(?::(?<packaging>[^:\\s]*)(?::(?<classifier>[^:\\s]+))?)?:(?<version>[^:\\s]+)$"
);
private final Consumer<String> addToClasspath;
private final List<DependencyResolver> repos;
public MavenResolver(Consumer<String> addToClasspath) {
this.addToClasspath = addToClasspath;
this.repos = new LinkedList<>();
this.repos.add(CommonRepositories.mavenCentral());
this.repos.add(CommonRepositories.mavenLocal());
}
public void addRemoteRepo(String name, String url) {
if (DEFAULT_RESOLVER_NAME.equals(name))
throw new IllegalArgumentException("Illegal repository name, cannot use '" + DEFAULT_RESOLVER_NAME + "'.");
this.repos.add(CommonRepositories.maven(name, url));
}
private ChainResolver searchAllReposResolver(Set<String> repos) {
ChainResolver resolver = new ChainResolver();
resolver.setName(DEFAULT_RESOLVER_NAME);
this.repos.stream()
.filter(r -> repos == null || repos.contains(r.getName().toLowerCase()))
.forEach(resolver::add);
if (repos != null) {
Set<String> resolverNames = resolver.getResolvers().stream()
.map(d -> d.getName().toLowerCase())
.collect(Collectors.toSet());
repos.removeAll(resolverNames);
repos.forEach(r -> {
try {
URL url = new URL(r);
resolver.add(CommonRepositories.maven("from-" + url.getHost(), r));
} catch (MalformedURLException e) {
// Ignore as we will assume that a bad url was a name
}
});
}
return resolver;
}
private static ModuleRevisionId parseCanonicalArtifactName(String canonical) {
Matcher m = IVY_MRID_PATTERN.matcher(canonical);
if (m.matches()) {
return ModuleRevisionId.newInstance(
m.group("organization"),
m.group("name"),
m.group("branch"),
m.group("revision")
);
}
m = MAVEN_MRID_PATTERN.matcher(canonical);
if (m.matches()) {
String packaging = m.group("packaging");
String classifier = m.group("classifier");
return ModuleRevisionId.newInstance(
m.group("group"),
m.group("artifact"),
m.group("version"),
packaging == null
? Collections.emptyMap()
: classifier == null
? Map.of("ext", packaging)
: Map.of("ext", packaging, "m:classifier", classifier)
);
}
throw new IllegalArgumentException("Cannot resolve '" + canonical + "' as maven or ivy coordinates.");
}
/**
* Create an ivy instance with the specified verbosity. The instance is relatively plain.
*
* @param verbosity the verbosity level.
* <ol start="0">
* <li>ERROR</li>
* <li>WANRING</li>
* <li>INFO</li>
* <li>VERBOSE</li>
* <li>DEBUG</li>
* </ol>
*
* @return the fresh ivy instance.
*/
private Ivy createDefaultIvyInstance(int verbosity) {
MessageLogger logger = new DefaultMessageLogger(verbosity);
// Set the default logger since not all things log to the ivy instance.
Message.setDefaultLogger(logger);
Ivy ivy = new Ivy();
ivy.getLoggerEngine().setDefaultLogger(logger);
ivy.setSettings(new IvySettings());
ivy.bind();
return ivy;
}
// TODO support multiple at once. This is necessary for conflict resolution with multiple overlapping dependencies.
// TODO support classpath resolution
public List<File> resolveMavenDependency(String canonical, Set<String> repos, int verbosity) throws IOException, ParseException {
ChainResolver rootResolver = this.searchAllReposResolver(repos);
Ivy ivy = this.createDefaultIvyInstance(verbosity);
IvySettings settings = ivy.getSettings();
settings.addResolver(rootResolver);
rootResolver.setCheckmodified(true);
settings.setDefaultResolver(rootResolver.getName());
ivy.getLoggerEngine().info("Searching for dependencies in: " + rootResolver.getResolvers());
ResolveOptions resolveOptions = new ResolveOptions();
resolveOptions.setTransitive(true);
resolveOptions.setDownload(true);
ModuleRevisionId artifactIdentifier = MavenResolver.parseCanonicalArtifactName(canonical);
DefaultModuleDescriptor containerModule = DefaultModuleDescriptor.newCallerInstance(
artifactIdentifier,
DEFAULT_RESOLVE_CONFS,
true, // Transitive
repos != null // Changing - the resolver will set this based on SNAPSHOT since they are all m2 compatible
// but if `repos` is specified, we want to force a lookup.
);
ResolveReport resolved = ivy.resolve(containerModule, resolveOptions);
if (resolved.hasError()) {
MessageLogger logger = ivy.getLoggerEngine();
Arrays.stream(resolved.getAllArtifactsReports())
.forEach(r -> {
logger.error("download " + r.getDownloadStatus() + ": " + r.getArtifact() + " of " + r.getType());
if (r.getArtifactOrigin() == null)
logger.error("\tCouldn't find artifact.");
else
logger.error("\tfrom: " + r.getArtifactOrigin());
});
// TODO better error...
throw new RuntimeException("Error resolving '" + canonical + "'. " + resolved.getAllProblemMessages());
}
return Arrays.stream(resolved.getAllArtifactsReports())
.filter(a -> JAR_TYPE.equalsIgnoreCase(a.getType()))
.map(ArtifactDownloadReport::getLocalFile)
.collect(Collectors.toList());
}
private File convertPomToIvy(Ivy ivy, File pomFile) throws IOException, ParseException {
PomModuleDescriptorParser parser = PomModuleDescriptorParser.getInstance();
URL pomUrl = pomFile.toURI().toURL();
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);
MessageLogger logger = ivy.getLoggerEngine();
logger.info(new String(Files.readAllBytes(tempIvyFile.toPath()), Charset.forName("utf8")));
return tempIvyFile;
}
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);
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, resolveOptions);
if (resolved.hasError())
// TODO better error...
throw new RuntimeException("Error resolving '" + ivyFile + "'. " + resolved.getAllProblemMessages());
return Arrays.stream(resolved.getAllArtifactsReports())
.map(ArtifactDownloadReport::getLocalFile)
.collect(Collectors.toList());
}
private String solidifyPartialPOM(String rawIn) throws ParserConfigurationException, IOException, SAXException, TransformerException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
// Wrap in a dummy tag to allow fragments
InputStream inStream = new SequenceInputStream(Collections.enumeration(Arrays.asList(
new ByteArrayInputStream("<ijava>".getBytes(Charset.forName("utf-8"))),
new ByteArrayInputStream(rawIn.getBytes(Charset.forName("utf-8"))),
new ByteArrayInputStream("</ijava>".getBytes(Charset.forName("utf-8")))
)));
Document doc = builder.parse(inStream);
NodeList rootChildren = doc.getDocumentElement().getChildNodes();
// If input was a single "project" tag then we don't touch it. It is assumed
// to be complete.
if (rootChildren.getLength() == 1 && "project".equalsIgnoreCase(rootChildren.item(0).getNodeName()))
return this.writeDOM(new DOMSource(rootChildren.item(0)));
// Put the pieces together and fill in the blanks.
Document fixed = builder.newDocument();
Node project = fixed.appendChild(fixed.createElement("project"));
Node dependencies = project.appendChild(fixed.createElement("dependencies"));
Node repositories = project.appendChild(fixed.createElement("repositories"));
boolean setModelVersion = false;
boolean setGroupId = false;
boolean setArtifactId = false;
boolean setVersion = false;
for (int i = 0; i < rootChildren.getLength(); i++) {
Node child = rootChildren.item(i);
switch (child.getNodeName()) {
case "modelVersion":
setModelVersion = true;
this.appendChildInNewDoc(child, fixed, project);
break;
case "groupId":
setGroupId = true;
this.appendChildInNewDoc(child, fixed, project);
break;
case "artifactId":
setArtifactId = true;
this.appendChildInNewDoc(child, fixed, project);
break;
case "version":
setVersion = true;
this.appendChildInNewDoc(child, fixed, project);
break;
case "dependency":
this.appendChildInNewDoc(child, fixed, dependencies);
break;
case "repository":
this.appendChildInNewDoc(child, fixed, repositories);
break;
case "dependencies":
// Add all dependencies to the collecting tag
NodeList dependencyChildren = child.getChildNodes();
for (int j = 0; j < dependencyChildren.getLength(); j++)
this.appendChildInNewDoc(dependencyChildren.item(j), fixed, dependencies);
break;
case "repositories":
// Add all repositories to the collecting tag
NodeList repositoryChildren = child.getChildNodes();
for (int j = 0; j < repositoryChildren.getLength(); j++)
this.appendChildInNewDoc(repositoryChildren.item(j), fixed, repositories);
break;
default:
this.appendChildInNewDoc(child, fixed, project);
break;
}
}
if (!setModelVersion) {
Node modelVersion = project.appendChild(fixed.createElement("modelVersion"));
modelVersion.setTextContent("4.0.0");
}
if (!setGroupId) {
Node groupId = project.appendChild(fixed.createElement("groupId"));
groupId.setTextContent("ijava.notebook");
}
if (!setArtifactId) {
Node artifactId = project.appendChild(fixed.createElement("artifactId"));
artifactId.setTextContent("cell");
}
if (!setVersion) {
Node version = project.appendChild(fixed.createElement("version"));
version.setTextContent("1");
}
return this.writeDOM(new DOMSource(fixed));
}
private void appendChildInNewDoc(Node oldNode, Document doc, Node newParent) {
Node newNode = oldNode.cloneNode(true);
doc.adoptNode(newNode);
newParent.appendChild(newNode);
}
private String writeDOM(Source src) throws TransformerException, UnsupportedEncodingException {
Transformer idTransformer = TransformerFactory.newInstance().newTransformer();
idTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
ByteArrayOutputStream out = new ByteArrayOutputStream();
Result dest = new StreamResult(out);
idTransformer.transform(src, dest);
return out.toString("utf-8");
}
public void addJarsToClasspath(Iterable<String> jars) {
jars.forEach(this.addToClasspath);
}
@LineMagic(aliases = { "addMavenDependency", "maven" })
public void addMavenDependencies(List<String> args) {
MagicsArgs schema = MagicsArgs.builder()
.varargs("deps")
.keyword("from")
.flag("verbose", 'v')
.onlyKnownKeywords()
.onlyKnownFlags()
.build();
Map<String, List<String>> vals = schema.parse(args);
List<String> deps = vals.get("deps");
List<String> from = vals.get("from");
int verbosity = vals.get("verbose").size();
Set<String> repos = from.isEmpty() ? null : new LinkedHashSet<>(from);
for (String dep : deps) {
try {
this.addJarsToClasspath(
this.resolveMavenDependency(dep, repos, verbosity).stream()
.map(File::getAbsolutePath)
::iterator
);
} catch (IOException | ParseException e) {
throw new RuntimeException(e);
}
}
}
@LineMagic(aliases = { "mavenRepo" })
public void addMavenRepo(List<String> args) {
MagicsArgs schema = MagicsArgs.builder().required("id").required("url").build();
Map<String, List<String>> vals = schema.parse(args);
String id = vals.get("id").get(0);
String url = vals.get("url").get(0);
this.addRemoteRepo(id, url);
}
@CellMagic
public void loadFromPOM(List<String> args, String body) throws Exception {
try {
File tempPomPath = File.createTempFile("ijava-maven-", ".pom").getAbsoluteFile();
tempPomPath.deleteOnExit();
String rawPom = this.solidifyPartialPOM(body);
Files.write(tempPomPath.toPath(), rawPom.getBytes(Charset.forName("utf-8")));
List<String> loadArgs = new ArrayList<>(args.size() + 1);
loadArgs.add(tempPomPath.getAbsolutePath());
loadArgs.addAll(args);
this.loadFromPOM(loadArgs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@LineMagic
public void loadFromPOM(List<String> args) {
if (args.isEmpty())
throw new IllegalArgumentException("Loading from POM requires at least the path to the POM file");
MagicsArgs schema = MagicsArgs.builder()
.required("pomPath")
.varargs("scopes")
.flag("verbose", 'v')
.onlyKnownKeywords().onlyKnownFlags().build();
Map<String, List<String>> vals = schema.parse(args);
String pomPath = vals.get("pomPath").get(0);
List<String> scopes = vals.get("scopes");
int verbosity = vals.get("verbose").size();
File pomFile = new File(pomPath);
try {
Ivy ivy = this.createDefaultIvyInstance(verbosity);
IvySettings settings = ivy.getSettings();
File ivyFile = this.convertPomToIvy(ivy, pomFile);
this.addPomReposToIvySettings(settings, pomFile);
this.addJarsToClasspath(
this.resolveFromIvyFile(ivy, ivyFile, scopes).stream()
.map(File::getAbsolutePath)
::iterator
);
} catch (IOException | ParseException | ModelBuildingException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,56 @@
package io.github.spencerpark.ijava.magics.dependencies;
import org.apache.ivy.plugins.resolver.DependencyResolver;
import org.apache.ivy.plugins.resolver.IBiblioResolver;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.nio.file.Path;
public class CommonRepositories {
protected static final String MAVEN_PATTERN_PREFIX = "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier])";
protected static final String MAVEN_ARTIFACT_PATTERN = MAVEN_PATTERN_PREFIX + ".[ext]";
protected static final String MAVEN_POM_PATTERN = MAVEN_PATTERN_PREFIX + ".pom";
public static DependencyResolver maven(String name, String urlRaw) {
IBiblioResolver resolver = new IBiblioResolver();
resolver.setM2compatible(true);
resolver.setUseMavenMetadata(true);
resolver.setUsepoms(true);
resolver.setRoot(urlRaw);
resolver.setName(name);
return resolver;
}
public static DependencyResolver mavenCentral() {
return CommonRepositories.maven("maven-central", "https://repo.maven.apache.org/maven2/");
}
public static DependencyResolver jcenter() {
return CommonRepositories.maven("jcenter", "https://jcenter.bintray.com/");
}
public static DependencyResolver mavenLocal() {
IBiblioResolver resolver = new IBiblioResolver();
resolver.setM2compatible(true);
resolver.setUseMavenMetadata(true);
resolver.setUsepoms(true);
resolver.setName("maven-local");
Path localRepoPath;
try {
localRepoPath = Maven.getInstance().getConfiguredLocalRepositoryPath();
} catch (IOException e) {
throw new RuntimeException("Error reading maven settings. " + e.getLocalizedMessage(), e);
} catch (SAXException e) {
throw new RuntimeException("Error parsing maven settings. " + e.getLocalizedMessage(), e);
}
resolver.setRoot("file:///" + localRepoPath.toString());
return resolver;
}
}

View File

@ -0,0 +1,214 @@
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 {
if (!Files.isRegularFile(settingsXmlPath))
return null;
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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,90 @@
package io.github.spencerpark.ijava.runtime;
import io.github.spencerpark.ijava.JavaKernel;
import io.github.spencerpark.jupyter.kernel.display.DisplayData;
import java.util.UUID;
public class Display {
public static DisplayData render(Object o) {
JavaKernel kernel = Kernel.getKernelInstance();
if (kernel != null) {
return kernel.getRenderer().render(o);
} else {
throw new RuntimeException("No IJava kernel running");
}
}
public static DisplayData render(Object o, String... as) {
JavaKernel kernel = Kernel.getKernelInstance();
if (kernel != null) {
return kernel.getRenderer().renderAs(o, as);
} else {
throw new RuntimeException("No IJava kernel running");
}
}
public static String display(Object o) {
JavaKernel kernel = Kernel.getKernelInstance();
if (kernel != null) {
DisplayData data = kernel.getRenderer().render(o);
String id = data.getDisplayId();
if (id == null) {
id = UUID.randomUUID().toString();
data.setDisplayId(id);
}
kernel.display(data);
return id;
} else {
throw new RuntimeException("No IJava kernel running");
}
}
public static String display(Object o, String... as) {
JavaKernel kernel = Kernel.getKernelInstance();
if (kernel != null) {
DisplayData data = kernel.getRenderer().renderAs(o, as);
String id = data.getDisplayId();
if (id == null) {
id = UUID.randomUUID().toString();
data.setDisplayId(id);
}
kernel.display(data);
return id;
} else {
throw new RuntimeException("No IJava kernel running");
}
}
public static void updateDisplay(String id, Object o) {
JavaKernel kernel = Kernel.getKernelInstance();
if (kernel != null) {
DisplayData data = kernel.getRenderer().render(o);
kernel.getIO().display.updateDisplay(id, data);
} else {
throw new RuntimeException("No IJava kernel running");
}
}
public static void updateDisplay(String id, Object o, String... as) {
JavaKernel kernel = Kernel.getKernelInstance();
if (kernel != null) {
DisplayData data = kernel.getRenderer().renderAs(o, as);
kernel.getIO().display.updateDisplay(id, data);
} else {
throw new RuntimeException("No IJava kernel running");
}
}
}

View File

@ -0,0 +1,20 @@
package io.github.spencerpark.ijava.runtime;
import io.github.spencerpark.ijava.IJava;
import io.github.spencerpark.ijava.JavaKernel;
public class Kernel {
public static JavaKernel getKernelInstance() {
return IJava.getKernelInstance();
}
public static Object eval(String expr) throws Exception {
JavaKernel kernel = getKernelInstance();
if (kernel != null) {
return kernel.evalRaw(expr);
} else {
throw new RuntimeException("No IJava kernel running");
}
}
}

View File

@ -0,0 +1,41 @@
package io.github.spencerpark.ijava.runtime;
import io.github.spencerpark.ijava.IJava;
import io.github.spencerpark.ijava.JavaKernel;
import io.github.spencerpark.jupyter.kernel.magic.registry.UndefinedMagicException;
import java.util.List;
public class Magics {
public static <T> T lineMagic(String name, List<String> args) {
JavaKernel kernel = IJava.getKernelInstance();
if (kernel != null) {
try {
return kernel.getMagics().applyLineMagic(name, args);
} catch (UndefinedMagicException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(String.format("Exception occurred while running line magic '%s': %s", name, e.getMessage()), e);
}
} else {
throw new RuntimeException("No IJava kernel running");
}
}
public static <T> T cellMagic(String name, List<String> args, String body) {
JavaKernel kernel = IJava.getKernelInstance();
if (kernel != null) {
try {
return kernel.getMagics().applyCellMagic(name, args, body);
} catch (UndefinedMagicException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(String.format("Exception occurred while running cell magic '%s': %s", name, e.getMessage()), e);
}
} else {
throw new RuntimeException("No IJava kernel running");
}
}
}

View File

@ -1,81 +0,0 @@
io.github.spencerpark.jupyter.kernel.display.DisplayData render(Object o) {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
return kernel.getRenderer().render(o);
} else {
throw new RuntimeException("No IJava kernel running");
}
}
io.github.spencerpark.jupyter.kernel.display.DisplayData render(Object o, String... as) {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
return kernel.getRenderer().renderAs(o, as);
} else {
throw new RuntimeException("No IJava kernel running");
}
}
String display(Object o) {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
io.github.spencerpark.jupyter.kernel.display.DisplayData data = kernel.getRenderer().render(o);
String id = data.getDisplayId();
if (id == null) {
id = java.util.UUID.randomUUID().toString();
data.setDisplayId(id);
}
kernel.display(data);
return id;
} else {
throw new RuntimeException("No IJava kernel running");
}
}
String display(Object o, String... as) {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
io.github.spencerpark.jupyter.kernel.display.DisplayData data = kernel.getRenderer().renderAs(o, as);
String id = data.getDisplayId();
if (id == null) {
id = java.util.UUID.randomUUID().toString();
data.setDisplayId(id);
}
kernel.display(data);
return id;
} else {
throw new RuntimeException("No IJava kernel running");
}
}
void updateDisplay(String id, Object o) {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
io.github.spencerpark.jupyter.kernel.display.DisplayData data = kernel.getRenderer().render(o);
kernel.getIO().display.updateDisplay(id, data);
} else {
throw new RuntimeException("No IJava kernel running");
}
}
void updateDisplay(String id, Object o, String... as) {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
io.github.spencerpark.jupyter.kernel.display.DisplayData data = kernel.getRenderer().renderAs(o, as);
kernel.getIO().display.updateDisplay(id, data);
} else {
throw new RuntimeException("No IJava kernel running");
}
}

View File

@ -1,9 +0,0 @@
Object eval(String expr) throws Exception {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
return kernel.evalRaw(expr);
} else {
throw new RuntimeException("No IJava kernel running");
}
}

View File

@ -6,6 +6,10 @@ import java.util.concurrent.*;
import java.util.prefs.*;
import java.util.regex.*;
import static io.github.spencerpark.ijava.runtime.Display.*;
import static io.github.spencerpark.ijava.runtime.Kernel.*;
import static io.github.spencerpark.ijava.runtime.Magics.*;
public void printf(String format, Object... args) {
System.out.printf(format, args);
}

View File

@ -1,31 +0,0 @@
<T> T lineMagic(String name, java.util.List<String> args) {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
try {
return kernel.getMagics().applyLineMagic(name, args);
} catch (io.github.spencerpark.jupyter.kernel.magic.registry.UndefinedMagicException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(String.format("Exception occurred while running line magic '%s': %s", name, e.getMessage()), e);
}
} else {
throw new RuntimeException("No IJava kernel running");
}
}
<T> T cellMagic(String name, java.util.List<String> args, String body) {
io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance();
if (kernel != null) {
try {
return kernel.getMagics().applyCellMagic(name, args, body);
} catch (io.github.spencerpark.jupyter.kernel.magic.registry.UndefinedMagicException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(String.format("Exception occurred while running cell magic '%s': %s", name, e.getMessage()), e);
}
} else {
throw new RuntimeException("No IJava kernel running");
}
}