diff --git a/plexus-compilers/plexus-compiler-aspectj/pom.xml b/plexus-compilers/plexus-compiler-aspectj/pom.xml index 38681fc4..2d56e4f3 100644 --- a/plexus-compilers/plexus-compiler-aspectj/pom.xml +++ b/plexus-compilers/plexus-compiler-aspectj/pom.xml @@ -14,7 +14,7 @@ AspectJ Compiler support for Plexus Compiler component. - 17 + 11 diff --git a/plexus-compilers/plexus-compiler-eclipse/pom.xml b/plexus-compilers/plexus-compiler-eclipse/pom.xml index 5017ceab..d05bb6b5 100644 --- a/plexus-compilers/plexus-compiler-eclipse/pom.xml +++ b/plexus-compilers/plexus-compiler-eclipse/pom.xml @@ -14,7 +14,7 @@ Eclipse Compiler support for Plexus Compiler component. - 17 + 11 @@ -57,4 +57,35 @@ + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + + + **/EclipseJavaCompilerDelegate.java + + + + + compile-java-17 + + compile + + + 17 + + **/EclipseJavaCompilerDelegate.java + + + + + + + + diff --git a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java index f14db674..4523919f 100644 --- a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java +++ b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java @@ -34,6 +34,9 @@ import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; @@ -52,8 +55,6 @@ import org.codehaus.plexus.compiler.CompilerOutputStyle; import org.codehaus.plexus.compiler.CompilerResult; import org.codehaus.plexus.util.StringUtils; -import org.eclipse.jdt.core.compiler.CompilationProgress; -import org.eclipse.jdt.core.compiler.batch.BatchCompiler; /** * @@ -63,12 +64,28 @@ public class EclipseJavaCompiler extends AbstractCompiler { public EclipseJavaCompiler() { super(CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE, ".java", ".class", null); + if (!isJdkSupported) return; + try { + // Do not directly import EclipseJavaCompilerDelegate or any ECJ classes compiled to target 17. + // This ensures that the plugin still runs on Java 11 and can report the error above. + Class delegateClass = Class.forName("org.codehaus.plexus.compiler.eclipse.EclipseJavaCompilerDelegate"); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodType getClassLoaderMT = MethodType.methodType(ClassLoader.class); + MethodType batchCompileMT = MethodType.methodType(boolean.class, List.class, PrintWriter.class); + getClassLoaderMH = lookup.findStatic(delegateClass, "getClassLoader", getClassLoaderMT); + batchCompileMH = lookup.findStatic(delegateClass, "batchCompile", batchCompileMT); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } } // ---------------------------------------------------------------------- // Compiler Implementation // ---------------------------------------------------------------------- + static boolean isJdkSupported = Runtime.version().feature() >= 17; boolean errorsAsWarnings = false; + private MethodHandle getClassLoaderMH; + private MethodHandle batchCompileMH; @Override public String getCompilerId() { @@ -77,6 +94,9 @@ public String getCompilerId() { @Override public CompilerResult performCompile(CompilerConfiguration config) throws CompilerException { + // Safeguard before using method handle accessing EclipseJavaCompilerDelegate + if (!isJdkSupported) throw new CompilerException("ECJ needs JRE 17+"); + List args = new ArrayList<>(); args.add("-noExit"); // Make sure ecj does not System.exit on us 8-/ @@ -324,31 +344,15 @@ public void report(Diagnostic diagnostic) { getLog().debug("ecj command line: " + args); - success = BatchCompiler.compile( - args.toArray(new String[args.size()]), devNull, devNull, new CompilationProgress() { - @Override - public void begin(int i) {} - - @Override - public void done() {} - - @Override - public boolean isCanceled() { - return false; - } - - @Override - public void setTaskName(String s) {} - - @Override - public void worked(int i, int i1) {} - }); + success = (boolean) batchCompileMH.invoke(args, devNull); getLog().debug(sw.toString()); if (errorF.length() < 80) { throw new EcjFailureException(sw.toString()); } messageList = new EcjResponseParser().parse(errorF, errorsAsWarnings); + } catch (Throwable e) { + throw new Exception(e); } finally { if (null != errorF) { try { @@ -507,15 +511,19 @@ private static boolean haveSourceOrReleaseArgument(List args) { return false; } - private JavaCompiler getEcj() { - ServiceLoader javaCompilerLoader = - ServiceLoader.load(JavaCompiler.class, BatchCompiler.class.getClassLoader()); + private JavaCompiler getEcj() throws CompilerException { + // Safeguard before using method handle accessing EclipseJavaCompilerDelegate + if (!isJdkSupported) throw new CompilerException("ECJ needs JRE 17+"); + ClassLoader classLoader; + try { + classLoader = (ClassLoader) getClassLoaderMH.invoke(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + ServiceLoader javaCompilerLoader = ServiceLoader.load(JavaCompiler.class, classLoader); Class c = null; try { - c = Class.forName( - "org.eclipse.jdt.internal.compiler.tool.EclipseCompiler", - false, - BatchCompiler.class.getClassLoader()); + c = Class.forName("org.eclipse.jdt.internal.compiler.tool.EclipseCompiler", false, classLoader); } catch (ClassNotFoundException e) { // Ignore } diff --git a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompilerDelegate.java b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompilerDelegate.java new file mode 100644 index 00000000..663e255d --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompilerDelegate.java @@ -0,0 +1,44 @@ +package org.codehaus.plexus.compiler.eclipse; + +import java.io.PrintWriter; +import java.util.List; + +import org.eclipse.jdt.core.compiler.CompilationProgress; +import org.eclipse.jdt.core.compiler.batch.BatchCompiler; + +/** + * Wraps API calls involving Java 17 class files from ECJ. {@link EclipseJavaCompiler} delegates to this class. + *

+ * Note: This class needs to be compiled with target 17, while all the other classes in this module can be + * compiled with target 11, as long as they do not directly import this class but use {@link Class#forName(String)} and + * method handles to invoke any methods from here. + */ +public class EclipseJavaCompilerDelegate { + static ClassLoader getClassLoader() { + return BatchCompiler.class.getClassLoader(); + } + + static boolean batchCompile(List args, PrintWriter devNull) { + return BatchCompiler.compile( + args.toArray(new String[0]), devNull, devNull, new BatchCompilerCompilationProgress()); + } + + private static class BatchCompilerCompilationProgress extends CompilationProgress { + @Override + public void begin(int i) {} + + @Override + public void done() {} + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public void setTaskName(String s) {} + + @Override + public void worked(int i, int i1) {} + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerUnsupportedJdkTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerUnsupportedJdkTest.java new file mode 100644 index 00000000..b19a4f8d --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerUnsupportedJdkTest.java @@ -0,0 +1,63 @@ +package org.codehaus.plexus.compiler.eclipse; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.io.File; +import java.util.Set; +import java.util.stream.Collectors; + +import org.codehaus.plexus.compiler.Compiler; +import org.codehaus.plexus.compiler.CompilerConfiguration; +import org.codehaus.plexus.compiler.CompilerException; +import org.codehaus.plexus.testing.PlexusTest; +import org.codehaus.plexus.util.FileUtils; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; + +import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@PlexusTest +@Isolated("changes static variable") +public class EclipseCompilerUnsupportedJdkTest { + static final boolean IS_JDK_SUPPORTED = EclipseJavaCompiler.isJdkSupported; + + @Inject + @Named("eclipse") + Compiler compiler; + + @BeforeAll + public static void setUpClass() { + EclipseJavaCompiler.isJdkSupported = false; + } + + @AfterAll + public static void cleanUpClass() { + EclipseJavaCompiler.isJdkSupported = IS_JDK_SUPPORTED; + } + + @Test + public void testUnsupportedJdk() { + CompilerException error = assertThrows(CompilerException.class, () -> compiler.performCompile(getConfig())); + MatcherAssert.assertThat(error.getMessage(), Matchers.containsString("ECJ needs JRE 17+")); + } + + private CompilerConfiguration getConfig() throws Exception { + String sourceDir = getBasedir() + "/src/test-input/src/main"; + Set sourceFiles = FileUtils.getFileNames(new File(sourceDir), "**/*.java", null, false, true).stream() + .map(File::new) + .collect(Collectors.toSet()); + CompilerConfiguration compilerConfig = new CompilerConfiguration(); + compilerConfig.addSourceLocation(sourceDir); + compilerConfig.setOutputLocation(getBasedir() + "/target/eclipse/classes"); + compilerConfig.setSourceFiles(sourceFiles); + compilerConfig.setTargetVersion("1.8"); + compilerConfig.setSourceVersion("1.8"); + return compilerConfig; + } +} diff --git a/pom.xml b/pom.xml index 13171985..1e36098a 100644 --- a/pom.xml +++ b/pom.xml @@ -182,7 +182,7 @@ maven-enforcer-plugin - enforce-java + enforce-maven-and-java-bytecode enforce @@ -192,6 +192,15 @@ [17,) [ERROR] OLD JDK [${java.version}] in use. This projects requires JDK 17 or newer + + 11 + + + org.aspectj:aspectjtools + org.eclipse.jdt:ecj + org.codehaus.plexus:plexus-compiler-eclipse + +