Skip to content
Open
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
13 changes: 13 additions & 0 deletions jitpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
before_install:
# Setup mise environment
- curl https://mise.run | sh
# the following installs the right tools and creates a mise.toml
# you can skip these if you commit a mise.toml to the project.
- ~/.local/bin/mise use java@17 maven@3.9


install:
# you could technically just run mvn install directly
# put just showing mise x as an option where you can
# configure additional jdks.
- ~/.local/bin/mise x -- mvn install -B -ntp -DskipTests
107 changes: 107 additions & 0 deletions jjava/src/main/java/org/dflib/jjava/magics/MavenResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import org.apache.maven.model.building.DefaultModelBuilderFactory;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.dflib.jjava.JJava;
import org.dflib.jjava.JavaKernel;
import org.dflib.jjava.jupyter.Extension;
import org.dflib.jjava.jupyter.ExtensionLoader;
import org.dflib.jjava.jupyter.kernel.magic.registry.CellMagic;
Expand All @@ -51,6 +53,9 @@
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
Expand Down Expand Up @@ -409,4 +414,106 @@ public void loadFromPOM(List<String> args) {
throw new RuntimeException(message, e);
}
}

/**
* Builds a JBang script and adds the resolved dependencies to the classpath.
* @param args
* @throws IOException
* @throws InterruptedException
*/
@LineMagic
public void jbang(List<String> args) throws IOException, InterruptedException {
if (args.isEmpty()) {
throw new IllegalArgumentException("Loading from JBang requires at least a path to a JBang script reference.");
}

MagicsArgs schema = MagicsArgs.builder()
.required("scriptRef")
.onlyKnownKeywords().onlyKnownFlags().build();

Map<String, List<String>> vals = schema.parse(args);
String scriptRef = vals.get("scriptRef").get(0);

ProcessBuilder pb = new ProcessBuilder("jbang", "build", scriptRef);
pb.redirectErrorStream(false);
Process process = pb.start();

int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("Building failed with exit code " + exitCode);
}

try {
List<String> resolvedDependencies = getJBangResolvedDependencies(scriptRef, null, true);
addJarsToClasspath(resolvedDependencies);
// let the kernel evaluate the body after the dependencies are resolved
// TODO: this is not right way as extensions can't give callback
//JavaKernel kernel = JJava.getKernproelInstance();
//kernel.eval(body);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@CellMagic("jbang")
public void handleJBang(List<String> args, String body) throws Exception {
try {
List<String> resolvedDependencies = getJBangResolvedDependencies("-", body,false);
addJarsToClasspath(resolvedDependencies);
// let the kernel evaluate the body after the dependencies are resolved
// TODO: this is not right way as extensions can't give callback
//JavaKernel kernel = JJava.getKernelInstance();
//kernel.eval(body);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

List<String> getJBangResolvedDependencies(String scriptRef, String body, boolean inclAppJar) throws IOException {
try {
ProcessBuilder pb = new ProcessBuilder("jbang", "info", "tools", scriptRef);
pb.redirectErrorStream(false);
Process process = pb.start();

if(body != null) {
try (java.io.OutputStream os = process.getOutputStream()) {
os.write(body.getBytes(java.nio.charset.StandardCharsets.UTF_8));
os.flush();
}
}

StringBuilder output = new StringBuilder();
try (java.io.InputStream is = process.getInputStream();
java.util.Scanner scanner = new java.util.Scanner(is, java.nio.charset.StandardCharsets.UTF_8.name())) {
while (scanner.hasNextLine()) {
output.append(scanner.nextLine()).append(System.lineSeparator());
}
}

int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("jbang info tools failed with exit code " + exitCode + ":\n" + output);
}


JsonObject json = JsonParser.parseString(output.toString()).getAsJsonObject();

List<String> resolvedDependencies = Collections.emptyList();
if (json.has("resolvedDependencies") && json.get("resolvedDependencies").isJsonArray()) {
resolvedDependencies = StreamSupport.stream(json.getAsJsonArray("resolvedDependencies").spliterator(), false)
.map(e -> e.getAsString())
.collect(Collectors.toList());
}

if(inclAppJar) {
resolvedDependencies.add(json.get("applicationJar").getAsString());
}

return resolvedDependencies;


} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,18 @@ void loadFromPOM() throws Exception {
assertThat(snippetResult.getStderr(), not(containsString("|")));
assertThat(snippetResult.getStdout(), containsString("jakarta.annotation.Nullable"));
}

@Test
void loadFromJBang() throws Exception {
String snippet = String.join("\n",
"%%jbang",
"//DEPS jakarta.annotation:jakarta.annotation-api:3.0.0",
"import jakarta.annotation.Nullable;",
"Nullable.class.getName()"
);
Container.ExecResult snippetResult = executeInKernel(snippet);

assertThat(snippetResult.getStderr(), not(containsString("|")));
assertThat(snippetResult.getStdout(), containsString("jakarta.annotation.Nullable"));
}
}
Loading