Skip to content

Commit b5b48fe

Browse files
Merge branch 'main' into fix-win-tests
2 parents b6034c1 + 75f84a5 commit b5b48fe

File tree

58 files changed

+774
-649
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+774
-649
lines changed

.github/workflows/maven.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
matrix:
1515
os: [ ubuntu-latest ]
16-
java: [ 11, 17, 21 ]
16+
java: [ 11, 17, 21, 25 ]
1717
include:
1818
- os: windows-latest
1919
java: 25

.github/workflows/release-prepare.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ jobs:
3535
env:
3636
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_jjava: ${{ github.ref_name }}
3737
run: |
38-
mvn clean verify -U
38+
mvn clean package -DskipTests -U
3939
python -m build
4040
4141
- name: Collect the release distributions
4242
run: |
4343
mkdir -p release/
44-
cp jjava/target/jjava-${{ github.ref_name }}-kernelspec.zip release/
44+
cp jjava-distro/target/jjava-${{ github.ref_name }}-kernelspec.zip release/
4545
cp -r dist/* release/
4646
4747
- name: Store the release distributions

RELEASE-NOTES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 1.0-a7
2+
3+
* #65 mutual dependent class not working
4+
* #67 JJAVA_STARTUP_SCRIPT can't start up spark
5+
* #102 Eval startup snippets explicitly
6+
* #105 Exploding classpath
7+
* #107 "java.class.path" uses an incorrect separator
8+
19
## 1.0-a6
210

311
* #68 CompilationException doesn't provide any information about the underlying problem
@@ -13,10 +21,13 @@
1321
* #82 Refactoring: Move JavaKernel init logic from constructor to Builder
1422
* #83 Simplify Kernel metadata loading approach
1523
* #84 Refactoring: reorg Maven modules for reusability
24+
* #85 Run extensions in JShell ClassLoader
1625
* #86 ineffecient / incorrect classpath handling
1726
* #89 Deprecate redundant magics: %jars, %addMavenDependency
1827
* #90 Deprecate Ivy artifact syntax for %maven magic
1928
* #93 Extensions / kernel lifecycle
29+
* #99 Deal with SLF4J transitive dependency
30+
* #100 Organize internal logging around SLF4J and JUL
2031

2132
## 1.0-a5
2233

jjava-distro/pom.xml

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
43
<modelVersion>4.0.0</modelVersion>
54

65
<parent>
@@ -23,10 +22,10 @@
2322
<artifactId>jjava-maven</artifactId>
2423
<version>${project.version}</version>
2524
</dependency>
25+
<!-- A few upstream libraries using SLF4J should be routed to JUL in the final kernel -->
2626
<dependency>
27-
<groupId>org.dflib.jjava</groupId>
28-
<artifactId>jjava-launcher</artifactId>
29-
<version>${project.version}</version>
27+
<groupId>org.slf4j</groupId>
28+
<artifactId>slf4j-jdk14</artifactId>
3029
</dependency>
3130

3231
<!-- Test dependencies -->
@@ -41,8 +40,8 @@
4140
<scope>test</scope>
4241
</dependency>
4342
<dependency>
44-
<groupId>org.slf4j</groupId>
45-
<artifactId>slf4j-simple</artifactId>
43+
<groupId>org.hamcrest</groupId>
44+
<artifactId>hamcrest</artifactId>
4645
<scope>test</scope>
4746
</dependency>
4847
</dependencies>
@@ -64,27 +63,44 @@
6463
<createDependencyReducedPom>true</createDependencyReducedPom>
6564
<relocations>
6665
<relocation>
67-
<!-- com.google.gson and com.google.errorprone packages -->
6866
<pattern>com.google</pattern>
6967
<shadedPattern>org.dflib.jjava.shaded.com.google</shadedPattern>
7068
</relocation>
7169
<relocation>
72-
<pattern>eu.neilalexander.jnacl</pattern>
73-
<shadedPattern>org.dflib.jjava.shaded.eu.neilalexander.jnacl</shadedPattern>
70+
<pattern>com.neilalexander</pattern>
71+
<shadedPattern>org.dflib.jjava.shaded.com.neilalexander</shadedPattern>
72+
</relocation>
73+
<relocation>
74+
<pattern>eu.maveniverse</pattern>
75+
<shadedPattern>org.dflib.jjava.shaded.eu.maveniverse</shadedPattern>
76+
</relocation>
77+
<relocation>
78+
<pattern>eu.neilalexander</pattern>
79+
<shadedPattern>org.dflib.jjava.shaded.eu.neilalexander</shadedPattern>
80+
</relocation>
81+
<relocation>
82+
<pattern>javax.inject</pattern>
83+
<shadedPattern>org.dflib.jjava.shaded.javax.inject</shadedPattern>
84+
</relocation>
85+
<relocation>
86+
<pattern>org.apache</pattern>
87+
<shadedPattern>org.dflib.jjava.shaded.org.apache</shadedPattern>
88+
</relocation>
89+
<relocation>
90+
<pattern>org.codehaus</pattern>
91+
<shadedPattern>org.dflib.jjava.shaded.org.codehaus</shadedPattern>
7492
</relocation>
7593
<relocation>
76-
<pattern>org.apache.commons.lang3</pattern>
77-
<shadedPattern>org.dflib.jjava.shaded.org.apache.commons.lang3</shadedPattern>
94+
<pattern>org.eclipse</pattern>
95+
<shadedPattern>org.dflib.jjava.shaded.org.eclipse</shadedPattern>
7896
</relocation>
7997
<relocation>
80-
<!-- multiple maven dependencies -->
81-
<pattern>org.apache.maven</pattern>
82-
<shadedPattern>org.dflib.jjava.shaded.org.apache.maven</shadedPattern>
98+
<pattern>org.slf4j</pattern>
99+
<shadedPattern>org.dflib.jjava.shaded.org.slf4j</shadedPattern>
83100
</relocation>
84101
<relocation>
85-
<!-- multiple plexus dependencies -->
86-
<pattern>org.codehaus.plexus</pattern>
87-
<shadedPattern>org.dflib.jjava.shaded.org.codehaus.plexus</shadedPattern>
102+
<pattern>org.sonatype</pattern>
103+
<shadedPattern>org.dflib.jjava.shaded.org.sonatype</shadedPattern>
88104
</relocation>
89105
<relocation>
90106
<pattern>org.zeromq</pattern>
@@ -106,17 +122,14 @@
106122
</filter>
107123
</filters>
108124
<transformers>
109-
<transformer
110-
implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer"/>
111-
<transformer
112-
implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
125+
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" />
126+
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
113127
<addHeader>false</addHeader>
114128
</transformer>
115129
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
116130
<resource>META-INF/sisu/javax.inject.Named</resource>
117131
</transformer>
118-
<transformer
119-
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
132+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
120133
<manifestEntries>
121134
<Main-Class>org.dflib.jjava.distro.JJava</Main-Class>
122135
</manifestEntries>

jjava-distro/src/main/java/org/dflib/jjava/distro/JJava.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,37 @@
11
package org.dflib.jjava.distro;
22

33
import org.dflib.jjava.jupyter.channels.JupyterConnection;
4-
import org.dflib.jjava.jupyter.channels.JupyterSocket;
54
import org.dflib.jjava.jupyter.kernel.KernelConnectionProperties;
65
import org.dflib.jjava.kernel.JavaKernel;
76
import org.dflib.jjava.kernel.magics.ClasspathMagic;
87
import org.dflib.jjava.kernel.magics.JarsMagic;
98
import org.dflib.jjava.kernel.magics.LoadCodeMagic;
10-
import org.dfllib.jjava.maven.MavenDependencyResolver;
11-
import org.dfllib.jjava.maven.magics.AddMavenDependencyMagic;
12-
import org.dfllib.jjava.maven.magics.LoadFromPomCellMagic;
13-
import org.dfllib.jjava.maven.magics.LoadFromPomLineMagic;
14-
import org.dfllib.jjava.maven.magics.MavenMagic;
15-
import org.dfllib.jjava.maven.magics.MavenRepoMagic;
9+
import org.dflib.jjava.maven.MavenDependencyResolver;
10+
import org.dflib.jjava.maven.magics.AddMavenDependencyMagic;
11+
import org.dflib.jjava.maven.magics.LoadFromPomCellMagic;
12+
import org.dflib.jjava.maven.magics.LoadFromPomLineMagic;
13+
import org.dflib.jjava.maven.magics.MavenMagic;
14+
import org.dflib.jjava.maven.magics.MavenRepoMagic;
1615

1716
import java.io.IOException;
1817
import java.io.InputStream;
1918
import java.nio.file.Files;
2019
import java.nio.file.Path;
2120
import java.nio.file.Paths;
2221
import java.util.Properties;
23-
import java.util.logging.Level;
2422

2523
/**
2624
* The main class launching Jupyter Java kernel.
2725
*/
2826
public class JJava {
2927

28+
// single-line JUL logging that mimics the default Jupyter format for the label:
29+
// [I 2025-10-19 16:15:41.181 ServerApp]
30+
private static final String JUL_JUPYTER_LOG_FORMAT = "[%4$.1s %1$tF %1$tT.%1$tL %3$s] %5$s%n";
3031
private static final String POM_PROPERTIES = "META-INF/maven/org.dflib.jjava/jjava-distro/pom.properties";
3132

3233
public static void main(String[] args) throws Exception {
34+
3335
if (args.length < 1) {
3436
throw new IllegalArgumentException("Missing connection file argument");
3537
}
@@ -39,7 +41,7 @@ public static void main(String[] args) throws Exception {
3941
throw new IllegalArgumentException("Connection file '" + connectionFile + "' isn't a file.");
4042
}
4143

42-
JupyterSocket.JUPYTER_LOGGER.setLevel(Level.WARNING);
44+
System.setProperty("java.util.logging.SimpleFormatter.format", JUL_JUPYTER_LOG_FORMAT);
4345

4446
KernelConnectionProperties connProps = KernelConnectionProperties.parse(Files.readString(connectionFile));
4547
JupyterConnection connection = new JupyterConnection(connProps);
@@ -53,7 +55,6 @@ public static void main(String[] args) throws Exception {
5355
.version((String) pomProps.getOrDefault("version", ""))
5456

5557
.extensionsEnabled(Env.extensionsEnabled())
56-
.startupSnippets(Env.startupSnippets())
5758
.compilerOpts(Env.compilerOpts())
5859
.timeout(Env.timeout())
5960

@@ -71,12 +72,15 @@ public static void main(String[] args) throws Exception {
7172

7273
.build();
7374

74-
// install built-in Extensions
75+
// default startup: init "BaseKernel.notebookKernel" and install the default extensions (if enabled)
7576
kernel.onStartup();
7677

77-
// add custom locations to JShell classpath, look for Extensions there, load and install them
78+
// process custom locations: expand JShell classpath, install extensions from those places (if enabled)
7879
kernel.addToClasspath(Env.extraClasspath());
7980

81+
// run user defined startup snippets explicitly after the default startup
82+
Env.startupSnippets().forEach(kernel::evalRaw);
83+
8084
// connect to Jupyter
8185
kernel.becomeHandlerForConnection(connection);
8286
connection.connect();

jjava-distro/src/main/java/org/dflib/jjava/distro/Opts.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
class Opts {
77

8-
// TODO: see 7205d03766c8 and c91ade8fec0cdc. A similar split method in MagicParser got altered in a few subtle
8+
// TODO: see 7205d03766c8 and c91ade8fec0cdc. A similar split method in MagicsResolver got altered in a few subtle
99
// ways... Do those changes alloy here, and should we have a single splitter?
1010
// 7205d03766c8
1111
// - if (current.length() > 0 && inQuotes) {

jjava-distro/src/test/java/org/dflib/jjava/distro/KernelStartupIT.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import org.junit.jupiter.api.Test;
44
import org.testcontainers.containers.Container;
55

6+
import java.util.Map;
7+
68
import static org.hamcrest.CoreMatchers.containsString;
79
import static org.hamcrest.CoreMatchers.not;
810
import static org.hamcrest.MatcherAssert.assertThat;
11+
import static org.hamcrest.Matchers.matchesPattern;
912

1013
public class KernelStartupIT extends ContainerizedKernelCase {
1114

@@ -17,4 +20,17 @@ public void startUp() throws Exception {
1720
assertThat(snippetResult.getStderr(), not(containsString("|")));
1821
assertThat(snippetResult.getStdout(), containsString("1001.0"));
1922
}
23+
24+
@Test
25+
void startUp_scriptRequiresClasspath() throws Exception {
26+
Map<String, String> env = Map.of(
27+
Env.JJAVA_CLASSPATH, TEST_CLASSPATH,
28+
Env.JJAVA_STARTUP_SCRIPT, "var obj = new org.dflib.jjava.Dummy()"
29+
);
30+
String snippet = "\"hash = \" + obj.hashCode()";
31+
Container.ExecResult snippetResult = executeInKernel(snippet, env);
32+
33+
assertThat(snippetResult.getStdout(), not(containsString("|")));
34+
assertThat(snippetResult.getStdout(), matchesPattern("(?s).*hash = -?\\d+.*"));
35+
}
2036
}

jjava-jupyter/pom.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
<groupId>org.zeromq</groupId>
2323
<artifactId>jeromq</artifactId>
2424
</dependency>
25+
<dependency>
26+
<groupId>org.slf4j</groupId>
27+
<artifactId>slf4j-api</artifactId>
28+
</dependency>
2529

2630
<dependency>
2731
<groupId>org.junit.jupiter</groupId>
@@ -35,7 +39,7 @@
3539
</dependency>
3640
<dependency>
3741
<groupId>org.hamcrest</groupId>
38-
<artifactId>hamcrest-all</artifactId>
42+
<artifactId>hamcrest</artifactId>
3943
<scope>test</scope>
4044
</dependency>
4145
<dependency>
@@ -45,4 +49,4 @@
4549
</dependency>
4650
</dependencies>
4751

48-
</project>
52+
</project>

jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/channels/HeartbeatChannel.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
import org.dflib.jjava.jupyter.kernel.KernelConnectionProperties;
44
import org.dflib.jjava.jupyter.messages.HMACGenerator;
5+
import org.slf4j.LoggerFactory;
56
import org.zeromq.SocketType;
67
import org.zeromq.ZMQ;
78

89
import java.util.concurrent.atomic.AtomicInteger;
9-
import java.util.logging.Level;
10-
import java.util.logging.Logger;
10+
1111

1212
public class HeartbeatChannel extends JupyterSocket {
13-
private static final long HB_DEFAULT_SLEEP_MS = 500;
1413

14+
private static final long HB_DEFAULT_SLEEP_MS = 500;
1515
private static final AtomicInteger HEARTBEAT_ID = new AtomicInteger();
1616

1717
private final long sleep;
1818
private volatile Loop pulse;
1919

2020
public HeartbeatChannel(ZMQ.Context context, HMACGenerator hmacGenerator, long sleep) {
21-
super(context, SocketType.REP, hmacGenerator, Logger.getLogger("HeartbeatChannel"));
21+
super(context, SocketType.REP, hmacGenerator, LoggerFactory.getLogger("HeartbeatChannel"));
2222
this.sleep = sleep;
2323
}
2424

@@ -38,7 +38,7 @@ public void bind(KernelConnectionProperties connProps) {
3838
String channelThreadName = "Heartbeat-" + HEARTBEAT_ID.getAndIncrement();
3939
String addr = formatAddress(connProps.getTransport(), connProps.getIp(), connProps.getHbPort());
4040

41-
logger.log(Level.INFO, String.format("Binding %s to %s.", channelThreadName, addr));
41+
logger.debug("Binding {} to {}.", channelThreadName, addr);
4242
super.bind(addr);
4343

4444
ZMQ.Poller poller = super.ctx.poller(1);
@@ -49,22 +49,24 @@ public void bind(KernelConnectionProperties connProps) {
4949
if (events > 0) {
5050
byte[] msg = this.recv();
5151
if (msg == null) {
52-
//Error during receive, just continue
53-
super.logger.log(Level.SEVERE, "Poll returned 1 event but could not read the echo string");
52+
// Error during receive, just continue
53+
logger.warn("Poll returned 1 event but could not read the echo string");
5454
return;
5555
}
56+
5657
if (!this.send(msg)) {
57-
super.logger.log(Level.SEVERE, "Could not send heartbeat reply");
58+
logger.warn("Could not send heartbeat reply");
5859
}
59-
super.logger.log(Level.FINEST, "Heartbeat pulse");
60+
61+
logger.trace("Heartbeat pulse");
6062
}
6163
});
6264
this.pulse.onClose(() -> {
63-
logger.log(Level.INFO, channelThreadName + " shutdown.");
65+
logger.debug( "{} shutdown.", channelThreadName);
6466
this.pulse = null;
6567
});
6668
this.pulse.start();
67-
logger.log(Level.INFO, "Polling on " + channelThreadName);
69+
logger.debug("Polling on {}", channelThreadName);
6870
}
6971

7072
@Override
@@ -80,7 +82,8 @@ public void waitUntilClose() {
8082
if (this.pulse != null) {
8183
try {
8284
this.pulse.join();
83-
} catch (InterruptedException ignored) { }
85+
} catch (InterruptedException ignored) {
86+
}
8487
}
8588
}
8689
}

0 commit comments

Comments
 (0)