From 0cb0360f55a10a13458e306b037c6392d5567ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Manuel=20M=C3=A9ndez=20Rey?= Date: Sat, 13 Jun 2026 18:22:56 +0200 Subject: [PATCH 1/2] Run on modern JDKs and jgit: Thread.stop, commit signing, console noise LessonRunner.stopAll(): Thread.stop() (deprecated for removal) made PLM's in-process recompilation of its framework sources fail on recent JDKs, so no Java exercise could run; use cooperative Thread.interrupt() instead. GitUtils: when the user's git config enables commit.gpgsign, jgit failed to sign PLM's internal tracking commits (ServiceUnavailableException) and dumped a stack trace to the console on every run; setSign(false) on them. LoggerPanel: show only compilation errors, not the deprecation/unchecked warnings from PLM's own framework sources, which are noise to the student. --- src/plm/core/model/LessonRunner.java | 11 ++++++++--- src/plm/core/model/tracking/GitUtils.java | 3 +++ src/plm/core/ui/LoggerPanel.java | 5 +++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/plm/core/model/LessonRunner.java b/src/plm/core/model/LessonRunner.java index f567d543a..ed3041e9d 100644 --- a/src/plm/core/model/LessonRunner.java +++ b/src/plm/core/model/LessonRunner.java @@ -103,12 +103,17 @@ public void run() { runners.remove(this); } - /** Stop all the threads that were already started. Harmful but who cares? */ - @SuppressWarnings("deprecation") + /** Stop all the threads that were already started. + * + * Thread.stop() was used here historically, but it is deprecated for removal: + * on recent JDKs its presence in this framework source -- which PLM recompiles + * in process when running a Java exercise -- makes that compilation fail, so + * no Java exercise can run. Replace it with Thread.interrupt(), a cooperative + * request to stop. */ public void stopAll() { while (runners.size()>0) { Thread t = runners.remove(runners.size() - 1); - t.stop(); // harmful but who cares ? + t.interrupt(); } } diff --git a/src/plm/core/model/tracking/GitUtils.java b/src/plm/core/model/tracking/GitUtils.java index 57fe3c749..dff97c10e 100644 --- a/src/plm/core/model/tracking/GitUtils.java +++ b/src/plm/core/model/tracking/GitUtils.java @@ -104,6 +104,7 @@ public void createInitialCommit() throws NoHeadException, NoMessageException, Un git.commit().setMessage("Empty initial commit") .setAuthor(new PersonIdent("John Doe", "john.doe@plm.net")) .setCommitter(new PersonIdent("John Doe", "john.doe@plm.net")) + .setSign(false) // never sign PLM's internal progress-tracking commits .call(); } @@ -159,6 +160,7 @@ else if(res.getMergeStatus() == MergeResult.MergeStatus.CONFLICTING) { git.commit().setMessage("Manual merging") .setAuthor(new PersonIdent("John Doe", "john.doe@plm.net")) .setCommitter(new PersonIdent("John Doe", "john.doe@plm.net")) + .setSign(false) // never sign PLM's internal progress-tracking commits .call(); } else if(res.getMergeStatus() == MergeResult.MergeStatus.FAILED) { @@ -339,6 +341,7 @@ public void commit(String msg) throws NoHeadException, NoMessageException, Unmer this.git.commit().setMessage(msg) .setAuthor(new PersonIdent("John Doe", "john.doe@plm.net")) .setCommitter(new PersonIdent("John Doe", "john.doe@plm.net")) + .setSign(false) // never sign PLM's internal progress-tracking commits .call(); } diff --git a/src/plm/core/ui/LoggerPanel.java b/src/plm/core/ui/LoggerPanel.java index 7f6a04d19..392f918aa 100644 --- a/src/plm/core/ui/LoggerPanel.java +++ b/src/plm/core/ui/LoggerPanel.java @@ -47,6 +47,11 @@ public void log(DiagnosticCollector diagnostics) { Pattern isJava6Pattern = Pattern.compile("major version 51 is newer than 50, the highest major version supported by this compiler"); for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + // Only show real errors. Warnings and notes here come from PLM's own + // framework sources, which are recompiled in process together with the + // student code; they are not the student's concern and only add noise. + if (diagnostic.getKind() != Diagnostic.Kind.ERROR) + continue; String source = diagnostic.getSource() == null ? "(null)" : diagnostic.getSource().getName(); String msg = diagnostic.getMessage(getLocale()); From dd2e2fb98980df9a870c921e9a8eee6026f0e6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Manuel=20M=C3=A9ndez=20Rey?= Date: Sat, 13 Jun 2026 19:26:05 +0200 Subject: [PATCH 2/2] Java mode: list() returns only compiled classes of the queried package The custom JavaFileManager.list() returned every in-process compiled class for any CLASS_PATH package query. javac trusts that list(package=P) returns classes of P and derives their binary name as P + the file's simple name, so a class such as plm.core.model.session.SourceFile leaked into the query for scala.reflect.internal.util and was read as scala.reflect.internal.util.SourceFile ("class file contains wrong class"), breaking any exercise whose recompiled set holds two classes with the same simple name (e.g. a Buggle Java exercise). Filter the compiled classes by the queried package, like the source loop just above. --- src/plm/core/lang/LangJava.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plm/core/lang/LangJava.java b/src/plm/core/lang/LangJava.java index 03b1d23f9..5e38c32dd 100644 --- a/src/plm/core/lang/LangJava.java +++ b/src/plm/core/lang/LangJava.java @@ -546,7 +546,16 @@ public Iterable list(Location location, String packageName, Set< if (file.getKind() == Kind.CLASS && file.getName().startsWith(packageName)) files.add(file); } - files.addAll(classLoader.files()); + // Only return the compiled classes that actually live in the queried + // package. javac trusts that list(package=P) returns classes of P and + // derives their binary name as P + simpleName; returning every compiled + // class unconditionally made it mistake e.g. plm...SourceFile for + // scala.reflect.internal.util.SourceFile ("class file contains wrong + // class") and broke compilation. + for (JavaFileObject file : classLoader.files()) { + if (file.getName().startsWith(packageName)) + files.add(file); + } } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) { for (JavaFileObject file : fileObjects.values()) { if (file.getKind() == Kind.SOURCE && file.getName().startsWith(packageName))