Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -547,10 +547,19 @@ protected void installDefaultExtensions() {
installExtensionsFromClassLoader(getClass().getClassLoader());
}

/**
* Returns the parent ClassLoader to use when loading extensions from classpath.
*
* @return the parent ClassLoader for extension loading
*/
protected ClassLoader getParentClassLoader() {
return ClassLoader.getSystemClassLoader();
}

/**
* Locates, loads and initializes {@code Extension}s. Extension classes are discovered via {@link ServiceLoader}.
* It is passed a custom ClassLoader created internally based on a combination of system ClassLoader and the extra
* classpath specified as an argument.
* It is passed a custom ClassLoader created internally based on a combination of the parent ClassLoader
* (from {@link #getParentClassLoader()}) and the extra classpath specified as an argument.
*
* @param classpath one or more filesystem paths separated by {@link java.io.File#pathSeparator}.
*/
Expand All @@ -561,7 +570,8 @@ protected void installExtensionsFromClasspath(String classpath) {
.map(BaseKernel::pathToURL)
.toArray(URL[]::new);

try (URLClassLoader classLoader = URLClassLoader.newInstance(urls)) {
ClassLoader parentClassLoader = getParentClassLoader();
try (URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader)) {
installExtensionsFromClassLoader(classLoader);
} catch (IOException e) {
throw new RuntimeException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,11 @@ public void interrupt() {
this.evaluator.interrupt();
}

@Override
protected ClassLoader getParentClassLoader() {
return evaluator.getJShellClassLoader();
}

/**
* @return a JShell instance used to evaluate Java code.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private Object doEval(String code) {
* Try to clean up information linked to a code snippet and the snippet itself
*/
private void dropSnippet(Snippet snippet) {
JJavaExecutionControl execControl = execControlProvider.getRegisteredControlByID(this.execControlID);
JJavaExecutionControl execControl = execControlProvider.getRegisteredControlByID(execControlID);
shell.drop(snippet);
// snippet.classFullName() returns name of a wrapper class created for a snippet
String className = snippetClassName(snippet);
Expand Down Expand Up @@ -272,4 +272,9 @@ public void interrupt() {
execControl.interrupt();
}
}

public ClassLoader getJShellClassLoader() {
JJavaExecutionControl execControl = execControlProvider.getRegisteredControlByID(execControlID);
return execControl != null ? execControl.getJShellClassLoader() : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ void unloadClass(String className) {
this.loaderDelegate.unloadClass(className);
}

public ClassLoader getJShellClassLoader() {
return loaderDelegate.getClassLoader();
}

@Override
public String toString() {
return "JJavaExecutionControl{" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ public void unloadClass(String name) {
declaredClasses.remove(name);
}

public ClassLoader getClassLoader() {
return classLoader;
}

class BytecodeClassLoader extends URLClassLoader {

public BytecodeClassLoader() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import org.junit.jupiter.api.Test;

import java.nio.file.Path;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class JavaKernelExtensionsLifecycleTest {
class JavaKernelExtensionsLifecycleTest {

@Test
public void defaultExtensions() {
void defaultExtension() {
assertNull(JavaNotebookStatics.kernel);

JavaKernel kernel = JavaKernel.builder().name("TestKernel").build();
Expand All @@ -28,16 +28,16 @@ public void defaultExtensions() {
}

@Test
void extraClasspathExtensions() throws Exception {
Path jar = TestJarFactory.buildJar(
"java/",
"java/org/dflib/jjava/kernel/test/TestExtension.java",
"java/META-INF/services/org.dflib.jjava.jupyter.Extension"
void extraClasspathExtension() throws Exception {
Path extensionJar = TestJarFactory.buildJar(
"extensions/classpath/",
"extensions/classpath/org/dflib/jjava/kernel/test/ExtraClasspathExtension.java",
"extensions/classpath/META-INF/services/org.dflib.jjava.jupyter.Extension"
);

String extraClasspath = PathsHandler.joinPaths(PathsHandler.resolveGlobs(jar.toAbsolutePath().toString()));
String extraClasspath = PathsHandler.joinPaths(List.of(extensionJar));

String extInstalledProp = "ext.installs:org.dflib.jjava.kernel.test.TestExtension";
String extInstalledProp = "ext.installs:org.dflib.jjava.kernel.test.ExtraClasspathExtension";
System.clearProperty(extInstalledProp);

JavaKernel kernel = JavaKernel
Expand All @@ -62,4 +62,63 @@ void extraClasspathExtensions() throws Exception {
assertNull(System.getProperty(extInstalledProp));
}

@Test
void evalExtension() throws Exception {
Path extensionJar = TestJarFactory.buildJar(
"extensions/eval/",
"extensions/eval/org/dflib/jjava/kernel/test/EvalExtension.java",
"extensions/eval/META-INF/services/org.dflib.jjava.jupyter.Extension"
);

String extraClasspath = PathsHandler.joinPaths(List.of(extensionJar));

JavaKernel kernel = JavaKernel
.builder()
.name("TestKernel")
.build();
try {
kernel.onStartup();
kernel.addToClasspath(extraClasspath);

Object installed = kernel.evalRaw("evalExtensionInstalled");
assertEquals(true, installed, "EvalExtension should have been installed");

Object result = kernel.evalRaw("evalValue");
assertEquals("Test message", result.toString(), "eval() call was not successful");
} finally {
kernel.onShutdown(false);
}
}

@Test
void libraryExtension() throws Exception {
Path libraryJar = TestJarFactory.buildJar(
"extensions/library/",
"extensions/library/org/dflib/jjava/kernel/test/TestLibraryClass.java"
);
Path extensionJar = TestJarFactory.buildJar(
"extensions/library/",
"extensions/library/org/dflib/jjava/kernel/test/ExternalLibraryExtension.java",
"extensions/library/META-INF/services/org.dflib.jjava.jupyter.Extension"
);

String extraClasspath = PathsHandler.joinPaths(List.of(libraryJar, extensionJar));

JavaKernel kernel = JavaKernel
.builder()
.name("TestKernel")
.build();
try {
kernel.onStartup();
kernel.addToClasspath(extraClasspath);

Object installed = kernel.evalRaw("externalLibraryExtensionInstalled");
assertEquals(true, installed, "ExternalLibraryExtension should have been installed");

Object result = kernel.evalRaw("externalLibraryValue");
assertEquals("Test message", result.toString(), "Library class method call was not successful");
} finally {
kernel.onShutdown(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.dflib.jjava.kernel.test.ExtraClasspathExtension
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import org.dflib.jjava.jupyter.Extension;
import org.dflib.jjava.jupyter.kernel.BaseKernel;
import org.dflib.jjava.kernel.JavaNotebookStatics;

public class TestExtension implements Extension {
public class ExtraClasspathExtension implements Extension {

@Override
public void install(BaseKernel kernel) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.dflib.jjava.kernel.test.EvalExtension
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.dflib.jjava.kernel.test;

import org.dflib.jjava.jupyter.Extension;
import org.dflib.jjava.jupyter.kernel.BaseKernel;

public class EvalExtension implements Extension {

@Override
public void install(BaseKernel kernel) {
kernel.eval("var evalValue = \"Test message\";");
kernel.eval("var evalExtensionInstalled = true;");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.dflib.jjava.kernel.test.ExternalLibraryExtension
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.dflib.jjava.kernel.test;

import org.dflib.jjava.jupyter.Extension;
import org.dflib.jjava.jupyter.kernel.BaseKernel;

public class ExternalLibraryExtension implements Extension {

@Override
public void install(BaseKernel kernel) {
Object value;
try {
Class<?> libClass = Class.forName("org.dflib.jjava.kernel.test.TestLibraryClass");
value = libClass.getMethod("getMessage").invoke(null);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}

kernel.eval("var externalLibraryValue = \"" + value + "\";");
kernel.eval("var externalLibraryExtensionInstalled = true;");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.dflib.jjava.kernel.test;

public class TestLibraryClass {

public static String getMessage() {
return "Test message";
}
}

This file was deleted.