diff --git a/.github/workflows/gradle-precommit.yml b/.github/workflows/gradle-precommit.yml index 13b4b644e59..33474baa036 100644 --- a/.github/workflows/gradle-precommit.yml +++ b/.github/workflows/gradle-precommit.yml @@ -21,7 +21,7 @@ jobs: # Operating systems to run on. os: [ubuntu-latest] # Test JVMs. - java: [ '11' ] + java: [ '17' ] steps: - uses: actions/checkout@v2 @@ -65,7 +65,7 @@ jobs: # macos-latest: a tad slower than ubuntu and pretty much the same (?) so leaving out. os: [ubuntu-latest] # Test JVMs. - java: [ '11' ] + java: [ '17' ] steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index 260bcbc08a7..f07d999b1de 100644 --- a/README.md +++ b/README.md @@ -39,16 +39,16 @@ comprehensive documentation, visit: ### Basic steps: - 0. Install OpenJDK 11 (or greater). + 0. Install OpenJDK 17 (or greater). 1. Clone Lucene's git repository (or download the source distribution). 2. Run gradle launcher script (`gradlew`). -### Step 0) Set up your development environment (OpenJDK 11 or greater) +### Step 0) Set up your development environment (OpenJDK 17 or greater) We'll assume that you know how to get and set up the JDK - if you don't, then we suggest starting at https://jdk.java.net/ and learning more about Java, before returning to this README. Lucene runs with -Java 11 or later. +Java 17 or later. Lucene uses [Gradle](https://gradle.org/) for build control. Gradle is itself Java-based and may be incompatible with newer Java versions; you can still build and test diff --git a/build.gradle b/build.gradle index 79a90cb5038..23a3b185dee 100644 --- a/build.gradle +++ b/build.gradle @@ -72,7 +72,7 @@ ext { } // Minimum Java version required to compile and run Lucene. - minJavaVersion = JavaVersion.VERSION_11 + minJavaVersion = JavaVersion.VERSION_17 // snapshot build marker used in scripts. snapshotBuild = version.contains("SNAPSHOT") diff --git a/buildSrc/scriptDepVersions.gradle b/buildSrc/scriptDepVersions.gradle index 19533b31ae2..12fda6be34c 100644 --- a/buildSrc/scriptDepVersions.gradle +++ b/buildSrc/scriptDepVersions.gradle @@ -23,7 +23,7 @@ ext { scriptDepVersions = [ "apache-rat": "0.11", "commons-codec": "1.13", - "ecj": "3.27.0", + "ecj": "3.28.0", "flexmark": "0.61.24", "javacc": "7.0.4", "jflex": "1.8.2", diff --git a/gradle/validation/spotless.gradle b/gradle/validation/spotless.gradle index be2a6d95787..0ee79cc0b07 100644 --- a/gradle/validation/spotless.gradle +++ b/gradle/validation/spotless.gradle @@ -37,7 +37,7 @@ configure(project(":lucene").subprojects) { prj -> lineEndings 'UNIX' endWithNewline() - googleJavaFormat('1.11.0') + googleJavaFormat('1.13.0') // Apply to all Java sources target "src/**/*.java" diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index ea2c6648259..01137e64330 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -32,6 +32,9 @@ Bug Fixes Other --------------------- +* LUCENE-10283: The minimum required Java version was bumped from 11 to 17. + (Adrien Grand, Uwe Schindler, Dawid Weiss, Robert Muir) + * LUCENE-10253: The @BadApple annotation has been removed from the test framework. (Adrien Grand) diff --git a/lucene/SYSTEM_REQUIREMENTS.md b/lucene/SYSTEM_REQUIREMENTS.md index bafcedb3e3a..b158d16b1c4 100644 --- a/lucene/SYSTEM_REQUIREMENTS.md +++ b/lucene/SYSTEM_REQUIREMENTS.md @@ -17,11 +17,11 @@ # System Requirements -Apache Lucene runs on Java 11 or greater. +Apache Lucene runs on Java 17 or greater. It is also recommended to always use the latest update version of your Java VM, because bugs may affect Lucene. An overview of known JVM bugs -can be found on http://wiki.apache.org/lucene-java/JavaBugs +can be found on https://cwiki.apache.org/confluence/display/LUCENE/JavaBugs With all Java versions it is strongly recommended to not use experimental `-XX` JVM options. diff --git a/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java b/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java index 5c20055c802..f31a3a1f880 100644 --- a/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java +++ b/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java @@ -334,8 +334,7 @@ public class MMapDirectory extends FSDirectory { private static final BufferCleaner CLEANER; static { - final Object hack = - AccessController.doPrivileged((PrivilegedAction) MMapDirectory::unmapHackImpl); + final Object hack = doPrivileged(MMapDirectory::unmapHackImpl); if (hack instanceof BufferCleaner) { CLEANER = (BufferCleaner) hack; UNMAP_SUPPORTED = true; @@ -348,6 +347,13 @@ public class MMapDirectory extends FSDirectory { } } + // Extracted to a method to be able to apply the SuppressForbidden annotation + @SuppressWarnings("removal") + @SuppressForbidden(reason = "security manager") + private static T doPrivileged(PrivilegedAction action) { + return AccessController.doPrivileged(action); + } + @SuppressForbidden(reason = "Needs access to sun.misc.Unsafe to enable hack") private static Object unmapHackImpl() { final Lookup lookup = lookup(); @@ -390,16 +396,15 @@ public class MMapDirectory extends FSDirectory { throw new IllegalArgumentException("unmapping only works with direct buffers"); } final Throwable error = - AccessController.doPrivileged( - (PrivilegedAction) - () -> { - try { - unmapper.invokeExact(buffer); - return null; - } catch (Throwable t) { - return t; - } - }); + doPrivileged( + () -> { + try { + unmapper.invokeExact(buffer); + return null; + } catch (Throwable t) { + return t; + } + }); if (error != null) { throw new IOException("Unable to unmap the mapped buffer: " + resourceDescription, error); } diff --git a/lucene/core/src/java/org/apache/lucene/util/NamedThreadFactory.java b/lucene/core/src/java/org/apache/lucene/util/NamedThreadFactory.java index 2d79c45040a..99d2c254aca 100644 --- a/lucene/core/src/java/org/apache/lucene/util/NamedThreadFactory.java +++ b/lucene/core/src/java/org/apache/lucene/util/NamedThreadFactory.java @@ -39,8 +39,7 @@ public class NamedThreadFactory implements ThreadFactory { * @param threadNamePrefix the name prefix assigned to each thread created. */ public NamedThreadFactory(String threadNamePrefix) { - final SecurityManager s = System.getSecurityManager(); - group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); + group = getThreadGroup(); this.threadNamePrefix = String.format( Locale.ROOT, @@ -49,6 +48,17 @@ public class NamedThreadFactory implements ThreadFactory { threadPoolNumber.getAndIncrement()); } + @SuppressWarnings("removal") + @SuppressForbidden(reason = "security manager") + private static ThreadGroup getThreadGroup() { + final SecurityManager s = System.getSecurityManager(); + if (s != null) { + return s.getThreadGroup(); + } else { + return Thread.currentThread().getThreadGroup(); + } + } + private static String checkPrefix(String prefix) { return prefix == null || prefix.length() == 0 ? "Lucene" : prefix; } diff --git a/lucene/core/src/java/org/apache/lucene/util/RamUsageEstimator.java b/lucene/core/src/java/org/apache/lucene/util/RamUsageEstimator.java index c7f225b95ef..e28e2a7f73c 100644 --- a/lucene/core/src/java/org/apache/lucene/util/RamUsageEstimator.java +++ b/lucene/core/src/java/org/apache/lucene/util/RamUsageEstimator.java @@ -584,9 +584,10 @@ public final class RamUsageEstimator { final Class target = clazz; final Field[] fields; try { - fields = - AccessController.doPrivileged((PrivilegedAction) target::getDeclaredFields); - } catch (AccessControlException e) { + fields = doPrivileged((PrivilegedAction) target::getDeclaredFields); + } catch ( + @SuppressWarnings("removal") + AccessControlException e) { throw new RuntimeException("Can't access fields of class: " + target, e); } @@ -599,6 +600,13 @@ public final class RamUsageEstimator { return alignObjectSize(size); } + // Extracted to a method to give the SuppressForbidden annotation the smallest possible scope + @SuppressWarnings("removal") + @SuppressForbidden(reason = "security manager") + private static T doPrivileged(PrivilegedAction action) { + return AccessController.doPrivileged(action); + } + /** Return shallow size of any array. */ private static long shallowSizeOfArray(Object array) { long size = NUM_BYTES_ARRAY_HEADER; diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/PositiveIntOutputs.java b/lucene/core/src/java/org/apache/lucene/util/fst/PositiveIntOutputs.java index 7e4fea6ec8c..a878cb4076f 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/PositiveIntOutputs.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/PositiveIntOutputs.java @@ -20,19 +20,15 @@ import java.io.IOException; import org.apache.lucene.store.DataInput; import org.apache.lucene.store.DataOutput; import org.apache.lucene.util.RamUsageEstimator; -import org.apache.lucene.util.SuppressForbidden; /** * An FST {@link Outputs} implementation where each output is a non-negative long value. * * @lucene.experimental */ -@SuppressForbidden(reason = "Uses a Long instance as a marker") public final class PositiveIntOutputs extends Outputs { - // Ignore the deprecated constructor. We do want a unique object here. - @SuppressWarnings({"all"}) - private static final Long NO_OUTPUT = new Long(0); + private static final Long NO_OUTPUT = 0L; private static final PositiveIntOutputs singleton = new PositiveIntOutputs(); diff --git a/lucene/core/src/test/org/apache/lucene/analysis/TestCharArraySet.java b/lucene/core/src/test/org/apache/lucene/analysis/TestCharArraySet.java index b25b4e8c5d0..b668be04478 100644 --- a/lucene/core/src/test/org/apache/lucene/analysis/TestCharArraySet.java +++ b/lucene/core/src/test/org/apache/lucene/analysis/TestCharArraySet.java @@ -56,20 +56,14 @@ public class TestCharArraySet extends LuceneTestCase { @SuppressForbidden(reason = "Explicitly checking new Integers") public void testObjectContains() { CharArraySet set = new CharArraySet(10, true); - Integer val = Integer.valueOf(1); - @SuppressWarnings("all") - Integer val1 = new Integer(1); - // Verify explicitly the case of different Integer instances - assertNotSame(val, val1); + Integer val = 1; set.add(val); assertTrue(set.contains(val)); - assertTrue(set.contains(val1)); // another integer assertTrue(set.contains("1")); assertTrue(set.contains(new char[] {'1'})); // test unmodifiable set = CharArraySet.unmodifiableSet(set); assertTrue(set.contains(val)); - assertTrue(set.contains(val1)); // another integer assertTrue(set.contains("1")); assertTrue(set.contains(new char[] {'1'})); } diff --git a/lucene/misc/src/java/org/apache/lucene/misc/store/HardlinkCopyDirectoryWrapper.java b/lucene/misc/src/java/org/apache/lucene/misc/store/HardlinkCopyDirectoryWrapper.java index 3361c2615d4..fd83daa0083 100644 --- a/lucene/misc/src/java/org/apache/lucene/misc/store/HardlinkCopyDirectoryWrapper.java +++ b/lucene/misc/src/java/org/apache/lucene/misc/store/HardlinkCopyDirectoryWrapper.java @@ -29,6 +29,7 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.FilterDirectory; import org.apache.lucene.store.IOContext; +import org.apache.lucene.util.SuppressForbidden; /** * This directory wrapper overrides {@link Directory#copyFrom(Directory, String, String, IOContext)} @@ -66,27 +67,26 @@ public final class HardlinkCopyDirectoryWrapper extends FilterDirectory { // only try hardlinks if we have permission to access the files // if not super.copyFrom() will give us the right exceptions suppressedException = - AccessController.doPrivileged( - (PrivilegedAction) - () -> { - try { - Files.createLink(toPath.resolve(destFile), fromPath.resolve(srcFile)); - } catch (FileNotFoundException - | NoSuchFileException - | FileAlreadyExistsException ex) { - return ex; // in these cases we bubble up since it's a true error condition. - } catch (IOException - // if the FS doesn't support hard-links - | UnsupportedOperationException - // we don't have permission to use hard-links just fall back to byte copy - | SecurityException ex) { - // hard-links are not supported or the files are on different filesystems - // we could go deeper and check if their filesstores are the same and opt - // out earlier but for now we just fall back to normal file-copy - return ex; - } - return null; - }); + doPrivileged( + () -> { + try { + Files.createLink(toPath.resolve(destFile), fromPath.resolve(srcFile)); + } catch (FileNotFoundException + | NoSuchFileException + | FileAlreadyExistsException ex) { + return ex; // in these cases we bubble up since it's a true error condition. + } catch (IOException + // if the FS doesn't support hard-links + | UnsupportedOperationException + // we don't have permission to use hard-links just fall back to byte copy + | SecurityException ex) { + // hard-links are not supported or the files are on different filesystems + // we could go deeper and check if their filesstores are the same and opt + // out earlier but for now we just fall back to normal file-copy + return ex; + } + return null; + }); tryCopy = suppressedException != null; } } @@ -101,4 +101,11 @@ public final class HardlinkCopyDirectoryWrapper extends FilterDirectory { } } } + + // Extracted to a method to give the SuppressForbidden annotation the smallest possible scope + @SuppressWarnings("removal") + @SuppressForbidden(reason = "security manager") + private static T doPrivileged(PrivilegedAction action) { + return AccessController.doPrivileged(action); + } } diff --git a/lucene/misc/src/java/org/apache/lucene/misc/util/fst/UpToTwoPositiveIntOutputs.java b/lucene/misc/src/java/org/apache/lucene/misc/util/fst/UpToTwoPositiveIntOutputs.java index b70c5c87195..c35a03dfc9f 100644 --- a/lucene/misc/src/java/org/apache/lucene/misc/util/fst/UpToTwoPositiveIntOutputs.java +++ b/lucene/misc/src/java/org/apache/lucene/misc/util/fst/UpToTwoPositiveIntOutputs.java @@ -20,7 +20,6 @@ import java.io.IOException; import org.apache.lucene.store.DataInput; import org.apache.lucene.store.DataOutput; import org.apache.lucene.util.RamUsageEstimator; -import org.apache.lucene.util.SuppressForbidden; import org.apache.lucene.util.fst.FSTCompiler; import org.apache.lucene.util.fst.Outputs; @@ -40,7 +39,6 @@ import org.apache.lucene.util.fst.Outputs; * * @lucene.experimental */ -@SuppressForbidden(reason = "Uses a Long instance as a marker") public final class UpToTwoPositiveIntOutputs extends Outputs { /** Holds two long outputs. */ @@ -76,9 +74,7 @@ public final class UpToTwoPositiveIntOutputs extends Outputs { } } - // Ignore the deprecated constructor. We do want a unique object here. - @SuppressWarnings({"all"}) - private static final Long NO_OUTPUT = new Long(0); + private static final Long NO_OUTPUT = 0L; private final boolean doShare; diff --git a/lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneTestCase.java index 48032cd39fb..16b5b5254dd 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneTestCase.java +++ b/lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneTestCase.java @@ -3103,6 +3103,8 @@ public abstract class LuceneTestCase extends Assert { * execution. If enabled, it needs the following {@link SecurityPermission}: {@code * "createAccessControlContext"} */ + @SuppressForbidden(reason = "security manager") + @SuppressWarnings("removal") public static T runWithRestrictedPermissions( PrivilegedExceptionAction action, Permission... permissions) throws Exception { assumeTrue( diff --git a/lucene/test-framework/src/java/org/apache/lucene/tests/util/RamUsageTester.java b/lucene/test-framework/src/java/org/apache/lucene/tests/util/RamUsageTester.java index 870509d011c..5248242af08 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/tests/util/RamUsageTester.java +++ b/lucene/test-framework/src/java/org/apache/lucene/tests/util/RamUsageTester.java @@ -323,46 +323,50 @@ public final class RamUsageTester { /** Create a cached information about shallow size and reference fields for a given class. */ @SuppressForbidden(reason = "We need to access private fields of measured objects.") private static ClassCache createCacheEntry(final Class clazz) { - return AccessController.doPrivileged( - (PrivilegedAction) - () -> { - ClassCache cachedInfo; - long shallowInstanceSize = RamUsageEstimator.NUM_BYTES_OBJECT_HEADER; - final ArrayList referenceFields = new ArrayList<>(32); - for (Class c = clazz; c != null; c = c.getSuperclass()) { - if (c == Class.class) { - // prevent inspection of Class' fields, throws SecurityException in Java 9! - continue; - } - final Field[] fields = c.getDeclaredFields(); - for (final Field f : fields) { - if (!Modifier.isStatic(f.getModifiers())) { - shallowInstanceSize = RamUsageEstimator.adjustForField(shallowInstanceSize, f); + @SuppressWarnings("removal") + ClassCache classCache = + AccessController.doPrivileged( + (PrivilegedAction) + () -> { + ClassCache cachedInfo; + long shallowInstanceSize = RamUsageEstimator.NUM_BYTES_OBJECT_HEADER; + final ArrayList referenceFields = new ArrayList<>(32); + for (Class c = clazz; c != null; c = c.getSuperclass()) { + if (c == Class.class) { + // prevent inspection of Class' fields, throws SecurityException in Java 9! + continue; + } + final Field[] fields = c.getDeclaredFields(); + for (final Field f : fields) { + if (!Modifier.isStatic(f.getModifiers())) { + shallowInstanceSize = + RamUsageEstimator.adjustForField(shallowInstanceSize, f); - if (!f.getType().isPrimitive()) { - try { - f.setAccessible(true); - referenceFields.add(f); - } catch (RuntimeException re) { - throw new RuntimeException( - String.format( - Locale.ROOT, - "Can't access field '%s' of class '%s' for RAM estimation.", - f.getName(), - clazz.getName()), - re); + if (!f.getType().isPrimitive()) { + try { + f.setAccessible(true); + referenceFields.add(f); + } catch (RuntimeException re) { + throw new RuntimeException( + String.format( + Locale.ROOT, + "Can't access field '%s' of class '%s' for RAM estimation.", + f.getName(), + clazz.getName()), + re); + } + } } } } - } - } - cachedInfo = - new ClassCache( - RamUsageEstimator.alignObjectSize(shallowInstanceSize), - referenceFields.toArray(new Field[referenceFields.size()])); - return cachedInfo; - }); + cachedInfo = + new ClassCache( + RamUsageEstimator.alignObjectSize(shallowInstanceSize), + referenceFields.toArray(new Field[referenceFields.size()])); + return cachedInfo; + }); + return classCache; } private static long byteArraySize(int len) { diff --git a/lucene/test-framework/src/java/org/apache/lucene/tests/util/TestSecurityManager.java b/lucene/test-framework/src/java/org/apache/lucene/tests/util/TestSecurityManager.java index 51b53127adc..6cef472447b 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/tests/util/TestSecurityManager.java +++ b/lucene/test-framework/src/java/org/apache/lucene/tests/util/TestSecurityManager.java @@ -19,6 +19,7 @@ package org.apache.lucene.tests.util; import java.lang.StackWalker.StackFrame; import java.util.Locale; import java.util.function.Predicate; +import org.apache.lucene.util.SuppressForbidden; /** * A {@link SecurityManager} that prevents tests calling {@link System#exit(int)}. Only the test @@ -28,6 +29,8 @@ import java.util.function.Predicate; *

Use this with {@code * -Djava.security.manager=org.apache.lucene.tests.util.TestSecurityManager}. */ +@SuppressForbidden(reason = "security manager") +@SuppressWarnings("removal") public final class TestSecurityManager extends SecurityManager { private static final String JUNIT4_TEST_RUNNER_PACKAGE = "com.carrotsearch.ant.tasks.junit4.";