Skip to content

Commit d372db6

Browse files
committed
Make Eclipse and AspectJ compilers run on JDK 11 again
AspectJ can compile many projects on JDK 11, because it is not always using Java 17 classes from JDT Core. If it does not work, there will be a runtime error, and the user can figure out what is wrong. For ECJ, the case is more complicated, because it directly imports Java 17 classes from JDT Core. There were isolated in new class EclipseJavaCompilerDelegate, which is accessed using Class::forName and method handles from EclipseJavaCompiler. I.e., Sisu Inject can scan the annotations on EclipseJavaCompiler on JDK 11, and there is no more confusing "No such compiler 'eclipse'" error, but rather: Error injecting constructor, ... EcjFailureException: Failed to run the ecj compiler: ECJ only works on Java 17+ at org.codehaus.plexus.compiler.eclipse.EclipseJavaCompiler.<init> This explicitly tells the user what is wrong. A multi-release JAR solution was tested and worked well in Maven, but was dismissed due to the terrible developer experience in IDEs. Fixes #347.
1 parent c5edddc commit d372db6

File tree

5 files changed

+116
-30
lines changed

5 files changed

+116
-30
lines changed

plexus-compilers/plexus-compiler-aspectj/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<description>AspectJ Compiler support for Plexus Compiler component.</description>
1515

1616
<properties>
17-
<javaVersion>17</javaVersion>
17+
<javaVersion>11</javaVersion>
1818
</properties>
1919

2020
<dependencies>

plexus-compilers/plexus-compiler-eclipse/pom.xml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<description>Eclipse Compiler support for Plexus Compiler component.</description>
1515

1616
<properties>
17-
<javaVersion>17</javaVersion>
17+
<javaVersion>11</javaVersion>
1818
</properties>
1919

2020
<dependencies>
@@ -57,4 +57,35 @@
5757
</dependency>
5858
</dependencies>
5959

60+
<build>
61+
<plugins>
62+
<plugin>
63+
<groupId>org.apache.maven.plugins</groupId>
64+
<artifactId>maven-compiler-plugin</artifactId>
65+
<executions>
66+
<execution>
67+
<id>default-compile</id>
68+
<configuration>
69+
<excludes>
70+
<exclude>**/EclipseJavaCompilerDelegate.java</exclude>
71+
</excludes>
72+
</configuration>
73+
</execution>
74+
<execution>
75+
<id>compile-java-17</id>
76+
<goals>
77+
<goal>compile</goal>
78+
</goals>
79+
<configuration>
80+
<release>17</release>
81+
<includes>
82+
<include>**/EclipseJavaCompilerDelegate.java</include>
83+
</includes>
84+
</configuration>
85+
</execution>
86+
</executions>
87+
</plugin>
88+
</plugins>
89+
</build>
90+
6091
</project>

plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
import java.io.File;
3535
import java.io.PrintWriter;
3636
import java.io.StringWriter;
37+
import java.lang.invoke.MethodHandle;
38+
import java.lang.invoke.MethodHandles;
39+
import java.lang.invoke.MethodType;
3740
import java.nio.charset.Charset;
3841
import java.nio.charset.IllegalCharsetNameException;
3942
import java.nio.charset.UnsupportedCharsetException;
@@ -52,8 +55,6 @@
5255
import org.codehaus.plexus.compiler.CompilerOutputStyle;
5356
import org.codehaus.plexus.compiler.CompilerResult;
5457
import org.codehaus.plexus.util.StringUtils;
55-
import org.eclipse.jdt.core.compiler.CompilationProgress;
56-
import org.eclipse.jdt.core.compiler.batch.BatchCompiler;
5758

5859
/**
5960
*
@@ -63,12 +64,27 @@
6364
public class EclipseJavaCompiler extends AbstractCompiler {
6465
public EclipseJavaCompiler() {
6566
super(CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE, ".java", ".class", null);
67+
if (Runtime.version().feature() < 17) throw new EcjFailureException("ECJ only works on Java 17+");
68+
try {
69+
// Do not directly import EclipseJavaCompilerDelegate or any ECJ classes compiled to target 17.
70+
// This ensures that the plugin still runs on Java 11 and can report the error above.
71+
Class<?> delegateClass = Class.forName("org.codehaus.plexus.compiler.eclipse.EclipseJavaCompilerDelegate");
72+
MethodHandles.Lookup lookup = MethodHandles.lookup();
73+
MethodType getClassLoaderMT = MethodType.methodType(ClassLoader.class);
74+
MethodType batchCompileMT = MethodType.methodType(boolean.class, List.class, PrintWriter.class);
75+
getClassLoaderMH = lookup.findStatic(delegateClass, "getClassLoader", getClassLoaderMT);
76+
batchCompileMH = lookup.findStatic(delegateClass, "batchCompile", batchCompileMT);
77+
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
78+
throw new RuntimeException(e);
79+
}
6680
}
6781

6882
// ----------------------------------------------------------------------
6983
// Compiler Implementation
7084
// ----------------------------------------------------------------------
7185
boolean errorsAsWarnings = false;
86+
private final MethodHandle getClassLoaderMH;
87+
private final MethodHandle batchCompileMH;
7288

7389
@Override
7490
public String getCompilerId() {
@@ -324,31 +340,15 @@ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
324340

325341
getLog().debug("ecj command line: " + args);
326342

327-
success = BatchCompiler.compile(
328-
args.toArray(new String[args.size()]), devNull, devNull, new CompilationProgress() {
329-
@Override
330-
public void begin(int i) {}
331-
332-
@Override
333-
public void done() {}
334-
335-
@Override
336-
public boolean isCanceled() {
337-
return false;
338-
}
339-
340-
@Override
341-
public void setTaskName(String s) {}
342-
343-
@Override
344-
public void worked(int i, int i1) {}
345-
});
343+
success = (boolean) batchCompileMH.invoke(args, devNull);
346344
getLog().debug(sw.toString());
347345

348346
if (errorF.length() < 80) {
349347
throw new EcjFailureException(sw.toString());
350348
}
351349
messageList = new EcjResponseParser().parse(errorF, errorsAsWarnings);
350+
} catch (Throwable e) {
351+
throw new Exception(e);
352352
} finally {
353353
if (null != errorF) {
354354
try {
@@ -508,14 +508,16 @@ private static boolean haveSourceOrReleaseArgument(List<String> args) {
508508
}
509509

510510
private JavaCompiler getEcj() {
511-
ServiceLoader<JavaCompiler> javaCompilerLoader =
512-
ServiceLoader.load(JavaCompiler.class, BatchCompiler.class.getClassLoader());
511+
ClassLoader classLoader;
512+
try {
513+
classLoader = (ClassLoader) getClassLoaderMH.invoke();
514+
} catch (Throwable e) {
515+
throw new RuntimeException(e);
516+
}
517+
ServiceLoader<JavaCompiler> javaCompilerLoader = ServiceLoader.load(JavaCompiler.class, classLoader);
513518
Class<?> c = null;
514519
try {
515-
c = Class.forName(
516-
"org.eclipse.jdt.internal.compiler.tool.EclipseCompiler",
517-
false,
518-
BatchCompiler.class.getClassLoader());
520+
c = Class.forName("org.eclipse.jdt.internal.compiler.tool.EclipseCompiler", false, classLoader);
519521
} catch (ClassNotFoundException e) {
520522
// Ignore
521523
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.codehaus.plexus.compiler.eclipse;
2+
3+
import java.io.PrintWriter;
4+
import java.util.List;
5+
6+
import org.eclipse.jdt.core.compiler.CompilationProgress;
7+
import org.eclipse.jdt.core.compiler.batch.BatchCompiler;
8+
9+
/**
10+
* Wraps API calls involving Java 17 class files from ECJ. {@link EclipseJavaCompiler} delegates to this class.
11+
* <p>
12+
* <b>Note:</b> This class needs to be compiled with target 17, while all the other classes in this module can be
13+
* compiled with target 11, as long as they do not directly import this class but use {@link Class#forName(String)} and
14+
* method handles to invoke any methods from here.
15+
*/
16+
public class EclipseJavaCompilerDelegate {
17+
static ClassLoader getClassLoader() {
18+
return BatchCompiler.class.getClassLoader();
19+
}
20+
21+
static boolean batchCompile(List<String> args, PrintWriter devNull) {
22+
return BatchCompiler.compile(
23+
args.toArray(new String[0]), devNull, devNull, new BatchCompilerCompilationProgress());
24+
}
25+
26+
private static class BatchCompilerCompilationProgress extends CompilationProgress {
27+
@Override
28+
public void begin(int i) {}
29+
30+
@Override
31+
public void done() {}
32+
33+
@Override
34+
public boolean isCanceled() {
35+
return false;
36+
}
37+
38+
@Override
39+
public void setTaskName(String s) {}
40+
41+
@Override
42+
public void worked(int i, int i1) {}
43+
}
44+
}

pom.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@
182182
<artifactId>maven-enforcer-plugin</artifactId>
183183
<executions>
184184
<execution>
185-
<id>enforce-java</id>
185+
<id>enforce-maven-and-java-bytecode</id>
186186
<goals>
187187
<goal>enforce</goal>
188188
</goals>
@@ -192,6 +192,15 @@
192192
<version>[17,)</version>
193193
<message>[ERROR] OLD JDK [${java.version}] in use. This projects requires JDK 17 or newer</message>
194194
</requireJavaVersion>
195+
<enforceBytecodeVersion>
196+
<maxJdkVersion>11</maxJdkVersion>
197+
<excludes>
198+
<!-- Java 17 -->
199+
<exclude>org.aspectj:aspectjtools</exclude>
200+
<exclude>org.eclipse.jdt:ecj</exclude>
201+
<exclude>org.codehaus.plexus:plexus-compiler-eclipse</exclude>
202+
</excludes>
203+
</enforceBytecodeVersion>
195204
</rules>
196205
</configuration>
197206
</execution>

0 commit comments

Comments
 (0)