From 0953f573e353856fb4f8dc0f4222e6810706e4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tibor=20Diga=C5=88a?= Date: Wed, 19 Jan 2022 21:45:03 +0100 Subject: [PATCH] [SUREFIRE-1975] JDK18 - The Security Manager is deprecated and will be removed in a future release --- .github/workflows/maven-verify.yml | 3 +- .../plugin/surefire/AbstractSurefireMojo.java | 5 +- .../src/site/apt/examples/junit.apt.vm | 3 + .../api/util/internal/ObjectUtils.java | 11 ++++ .../org/apache/maven/JUnit4SuiteTest.java | 4 +- .../api/util/internal/ObjectUtilsTest.java | 39 +++++++++++++ .../surefire/booter/ProviderFactory.java | 3 +- .../maven/surefire/booter/SystemUtils.java | 50 ++-------------- .../surefire/booter/SystemUtilsTest.java | 12 ---- .../jiras/Surefire34SecurityManagerIT.java | 9 +++ .../maven/surefire/junit/JUnit3Provider.java | 26 ++++++--- .../surefire/junit/JUnitTestSetTest.java | 58 +++++++++++++++++++ 12 files changed, 153 insertions(+), 70 deletions(-) create mode 100644 surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ObjectUtilsTest.java diff --git a/.github/workflows/maven-verify.yml b/.github/workflows/maven-verify.yml index 3e89f82c4d..3b0adba156 100644 --- a/.github/workflows/maven-verify.yml +++ b/.github/workflows/maven-verify.yml @@ -26,6 +26,7 @@ jobs: name: Verify uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v2 with: + jdk-matrix: '[ "8", "11", "17", "18-ea" ]' ff-goal: 'clean install -nsu -P run-its' ff-site-goal: 'clean install site -nsu -DskipTests -P reporting' verify-goal: 'clean install -nsu -P run-its' @@ -35,5 +36,5 @@ jobs: surefire-its/target/*/log.txt surefire-its/target/**/surefire-reports/* surefire-its/target/**/failsafe-reports/* - max-parallel: 4 + max-parallel: 5 timeout-minutes: 420 diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java index 33f83d006a..24bf9afd59 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java @@ -141,12 +141,12 @@ import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray; import static org.apache.maven.surefire.api.util.ReflectionUtils.tryGetMethod; import static org.apache.maven.surefire.booter.Classpath.emptyClasspath; -import static org.apache.maven.surefire.booter.SystemUtils.JAVA_SPECIFICATION_VERSION; import static org.apache.maven.surefire.booter.SystemUtils.endsWithJavaPath; import static org.apache.maven.surefire.booter.SystemUtils.isBuiltInJava9AtLeast; import static org.apache.maven.surefire.booter.SystemUtils.isJava9AtLeast; import static org.apache.maven.surefire.booter.SystemUtils.toJdkHomeFromJvmExec; import static org.apache.maven.surefire.booter.SystemUtils.toJdkVersionFromReleaseFile; +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT; import static org.apache.maven.surefire.shared.lang3.StringUtils.substringBeforeLast; import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS; import static org.apache.maven.surefire.shared.utils.StringUtils.capitalizeFirstLetter; @@ -2673,8 +2673,7 @@ else if ( !getEnvironmentVariables().containsKey( "JAVA_HOME" ) ) // use the same JVM as the one used to run Maven (the "java.home" one) String jvmToUse = System.getProperty( "java.home" ) + File.separator + "bin" + File.separator + "java"; - getConsoleLogger().debug( "Using JVM: " + jvmToUse + " with Java version " - + JAVA_SPECIFICATION_VERSION.toPlainString() ); + getConsoleLogger().debug( "Using JVM: " + jvmToUse + " with Java version " + JAVA_RECENT ); return new JdkAttributes( jvmToUse, isBuiltInJava9AtLeast() ); } diff --git a/maven-surefire-plugin/src/site/apt/examples/junit.apt.vm b/maven-surefire-plugin/src/site/apt/examples/junit.apt.vm index 52b2d94480..4352e1cdd2 100644 --- a/maven-surefire-plugin/src/site/apt/examples/junit.apt.vm +++ b/maven-surefire-plugin/src/site/apt/examples/junit.apt.vm @@ -204,6 +204,9 @@ else As long as <<>> is not 0 and you use JUnit3, you can run your tests with a Java security manager enabled. The class name of the security manager must be sent as a system property variable to the JUnit3 provider. + The JDK 17 deprecated the class <<>> and the security manager is not fully supported + since JDK 18. The JUnit3 provider fails with enabled system property <>> in JDK 18+. + JUnit4 uses mechanisms internally that are not compatible with the tested security managers and thus this means of configuring a security manager with JUnit4 is not supported by Surefire. diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/ObjectUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/ObjectUtils.java index b316be2cb0..9ee7571e43 100644 --- a/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/ObjectUtils.java +++ b/surefire-api/src/main/java/org/apache/maven/surefire/api/util/internal/ObjectUtils.java @@ -22,6 +22,9 @@ import java.lang.management.ManagementFactory; import java.util.Map; +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_17; +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT; + /** * Similar to Java 7 java.util.Objects. * @@ -44,4 +47,12 @@ public static Map systemProps() { return ManagementFactory.getRuntimeMXBean().getSystemProperties(); } + + /** + * @return true if SecurityManager is supported (even if deprecated) in JDK (up to 17) + */ + public static boolean isSecurityManagerSupported() + { + return JAVA_RECENT.atMost( JAVA_17 ); + } } diff --git a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java index c707ed6040..016cce4ff0 100644 --- a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java +++ b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java @@ -42,6 +42,7 @@ import org.apache.maven.surefire.api.util.internal.ChannelsWriterTest; import org.apache.maven.surefire.api.util.internal.ConcurrencyUtilsTest; import org.apache.maven.surefire.api.util.internal.ImmutableMapTest; +import org.apache.maven.surefire.api.util.internal.ObjectUtilsTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -72,7 +73,8 @@ ChannelsWriterTest.class, AsyncSocketTest.class, AbstractStreamEncoderTest.class, - AbstractStreamDecoderTest.class + AbstractStreamDecoderTest.class, + ObjectUtilsTest.class } ) @RunWith( Suite.class ) public class JUnit4SuiteTest diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ObjectUtilsTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ObjectUtilsTest.java new file mode 100644 index 0000000000..7d3c0a9753 --- /dev/null +++ b/surefire-api/src/test/java/org/apache/maven/surefire/api/util/internal/ObjectUtilsTest.java @@ -0,0 +1,39 @@ +package org.apache.maven.surefire.api.util.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.junit.Test; + +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_17; +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT; +import static org.fest.assertions.Assertions.assertThat; + +/** + * @since 3.0.0-M6 + */ +public class ObjectUtilsTest +{ + @Test + public void shouldSupportSecurityManager() + { + assertThat( ObjectUtils.isSecurityManagerSupported() ) + .isEqualTo( JAVA_RECENT.atMost( JAVA_17 ) ); + } +} diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java index 86b430ed3b..43461df686 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java @@ -32,6 +32,7 @@ import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeGetter; import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray; import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray2; +import static org.apache.maven.surefire.api.util.internal.ObjectUtils.isSecurityManagerSupported; /** * Creates the surefire provider. @@ -85,7 +86,7 @@ public static RunResult invokeProvider( Object testSet, ClassLoader testsClassLo } finally { - if ( restoreStreams && System.getSecurityManager() == null ) + if ( restoreStreams && ( !isSecurityManagerSupported() || System.getSecurityManager() == null ) ) { System.setOut( orgSystemOut ); System.setErr( orgSystemErr ); diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SystemUtils.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SystemUtils.java index 50467e2602..674cb22edc 100644 --- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SystemUtils.java +++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/SystemUtils.java @@ -33,17 +33,18 @@ import java.util.Properties; import java.util.StringTokenizer; -import static java.lang.Character.isDigit; import static java.lang.Thread.currentThread; import static java.util.Objects.requireNonNull; import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray; +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT; +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_9; import static org.apache.maven.surefire.shared.lang3.StringUtils.isNumeric; -import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_FREE_BSD; +import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodChain; +import static org.apache.maven.surefire.api.util.ReflectionUtils.tryLoadClass; import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_LINUX; +import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_FREE_BSD; import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_NET_BSD; import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_OPEN_BSD; -import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodChain; -import static org.apache.maven.surefire.api.util.ReflectionUtils.tryLoadClass; /** * JDK 9 support. @@ -53,8 +54,6 @@ */ public final class SystemUtils { - public static final BigDecimal JAVA_SPECIFICATION_VERSION = getJavaSpecificationVersion(); - private static final BigDecimal JIGSAW_JAVA_VERSION = new BigDecimal( 9 ).stripTrailingZeros(); private static final int PROC_STATUS_PID_FIRST_CHARS = 20; @@ -165,43 +164,6 @@ else if ( versions.countTokens() >= 2 ) } } - /** - * Safely extracts major and minor version as fractional number from - *
-     *     $MAJOR.$MINOR.$SECURITY
-     * 
. - *
- * The security version is usually not needed to know. - * It can be applied to not certified JRE. - * - * @return major.minor version derived from java specification version of this JVM, e.g. 1.8, 9, etc. - */ - private static BigDecimal getJavaSpecificationVersion() - { - StringBuilder fractionalVersion = new StringBuilder( "0" ); - for ( char c : org.apache.maven.surefire.shared.lang3.SystemUtils.JAVA_SPECIFICATION_VERSION.toCharArray() ) - { - if ( isDigit( c ) ) - { - fractionalVersion.append( c ); - } - else if ( c == '.' ) - { - if ( fractionalVersion.indexOf( "." ) == -1 ) - { - fractionalVersion.append( '.' ); - } - else - { - break; - } - } - } - String majorMinorVersion = fractionalVersion.toString(); - return new BigDecimal( majorMinorVersion.endsWith( "." ) ? majorMinorVersion + "0" : majorMinorVersion ) - .stripTrailingZeros(); - } - public static boolean isJava9AtLeast( String jvmExecutablePath ) { File externalJavaHome = toJdkHomeFromJvmExec( jvmExecutablePath ); @@ -220,7 +182,7 @@ public static boolean isJava9AtLeast( String jvmExecutablePath ) public static boolean isBuiltInJava9AtLeast() { - return JAVA_SPECIFICATION_VERSION.compareTo( JIGSAW_JAVA_VERSION ) >= 0; + return JAVA_RECENT.atLeast( JAVA_9 ); } public static boolean isJava9AtLeast( BigDecimal version ) diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SystemUtilsTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SystemUtilsTest.java index c504e04d9d..5919dd22e7 100644 --- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SystemUtilsTest.java +++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SystemUtilsTest.java @@ -46,7 +46,6 @@ import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.reflect.Whitebox.invokeMethod; /** * Test of {@link SystemUtils}. @@ -63,17 +62,6 @@ public class SystemUtilsTest */ public static class PlainUnitTests { - - @Test - public void shouldMatchJavaSpecVersion() throws Exception - { - BigDecimal actual = invokeMethod( SystemUtils.class, "getJavaSpecificationVersion" ); - BigDecimal expected = - new BigDecimal( System.getProperty( "java.specification.version" ) ).stripTrailingZeros(); - assertThat( actual ).isEqualTo( expected ); - assertThat( SystemUtils.JAVA_SPECIFICATION_VERSION ).isEqualTo( expected ); - } - @Test public void shouldParseProprietaryReleaseFile() throws IOException { diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire34SecurityManagerIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire34SecurityManagerIT.java index bb80aecdf5..1138546ff0 100644 --- a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire34SecurityManagerIT.java +++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire34SecurityManagerIT.java @@ -21,8 +21,11 @@ import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase; import org.apache.maven.surefire.its.fixture.SurefireLauncher; +import org.junit.BeforeClass; import org.junit.Test; +import static org.apache.maven.surefire.its.fixture.HelperAssertions.assumeJavaMaxVersion; + /** * SUREFIRE-621 Asserts proper test counts when running junit 3 tests in parallel * @@ -31,6 +34,12 @@ public class Surefire34SecurityManagerIT extends SurefireJUnit4IntegrationTestCase { + @BeforeClass + public static void checkJavaVersion() + { + assumeJavaMaxVersion( 17 ); + } + @Test public void testSecurityManager() { diff --git a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java index f7c2c0c6fe..ff210eccea 100644 --- a/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java +++ b/surefire-providers/surefire-junit3/src/main/java/org/apache/maven/surefire/junit/JUnit3Provider.java @@ -31,13 +31,14 @@ import org.apache.maven.surefire.api.report.TestSetReportEntry; import org.apache.maven.surefire.api.suite.RunResult; import org.apache.maven.surefire.api.testset.TestSetFailedException; -import org.apache.maven.surefire.api.util.ReflectionUtils; import org.apache.maven.surefire.api.util.RunOrderCalculator; import org.apache.maven.surefire.api.util.ScanResult; import org.apache.maven.surefire.api.util.TestsToRun; import java.util.Map; +import static org.apache.maven.surefire.api.util.ReflectionUtils.instantiate; +import static org.apache.maven.surefire.api.util.internal.ObjectUtils.isSecurityManagerSupported; import static org.apache.maven.surefire.api.util.internal.ObjectUtils.systemProps; /** @@ -100,13 +101,7 @@ else if ( forkTestSet instanceof Class ) final RunListener reporter = reporterFactory.createReporter(); ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) reporter ); Map systemProperties = systemProps(); - String smClassName = System.getProperty( "surefire.security.manager" ); - if ( smClassName != null ) - { - SecurityManager securityManager = - ReflectionUtils.instantiate( getClass().getClassLoader(), smClassName, SecurityManager.class ); - System.setSecurityManager( securityManager ); - } + setSystemManager( System.getProperty( "surefire.security.manager" ) ); for ( Class clazz : testsToRun ) { @@ -121,6 +116,21 @@ else if ( forkTestSet instanceof Class ) return runResult; } + static void setSystemManager( String smClassName ) throws TestSetFailedException + { + if ( smClassName != null ) + { + if ( !isSecurityManagerSupported() ) + { + throw new TestSetFailedException( "JDK does not support overriding Security Manager with " + + "a value in system property 'surefire.security.manager'." ); + } + ClassLoader classLoader = JUnit3Provider.class.getClassLoader(); + SecurityManager sm = instantiate( classLoader, smClassName, SecurityManager.class ); + System.setSecurityManager( sm ); + } + } + private SurefireTestSet createTestSet( Class clazz ) { return reflector.isJUnit3Available() && jUnit3TestChecker.accept( clazz ) diff --git a/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java b/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java index 594dcc5b15..67d1ece40b 100644 --- a/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java +++ b/surefire-providers/surefire-junit3/src/test/java/org/apache/maven/surefire/junit/JUnitTestSetTest.java @@ -22,15 +22,22 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.apache.maven.surefire.api.testset.TestSetFailedException; import org.apache.maven.surefire.common.junit3.JUnit3Reflector; import org.apache.maven.surefire.api.report.ReportEntry; import org.apache.maven.surefire.api.report.RunListener; import org.apache.maven.surefire.api.report.RunMode; import org.apache.maven.surefire.api.report.TestSetReportEntry; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_17; +import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT; + /** * */ @@ -54,6 +61,57 @@ public void testExecuteSuiteClass() succeededTests.get( 0 ).getName() ); } + public void testSystemManager() + { + boolean isDeprecated = !JAVA_RECENT.atMost( JAVA_17 ); + Object originalSm = null; + try + { + if ( !isDeprecated ) + { + originalSm = System.getSecurityManager(); + } + + JUnit3Provider.setSystemManager( "java.lang.SecurityManager" ); + + if ( isDeprecated ) + { + fail(); + } + + Object sm = System.getSecurityManager(); + assertNotNull( sm ); + assertEquals( "java.lang.SecurityManager", sm.getClass().getName() ); + assertNotSame( originalSm, sm ); + } + catch ( TestSetFailedException e ) + { + if ( !isDeprecated ) + { + fail(); + } + } + finally + { + if ( !isDeprecated ) + { + try + { + SecurityManager sm = (SecurityManager) originalSm; + AccessController.doPrivileged( (PrivilegedAction) () -> + { + System.setSecurityManager( sm ); + return null; + } ); + } + catch ( AccessControlException e ) + { + // ignore + } + } + } + } + /** * */