From 29ca754ab79ecd3f4e693e6f238d5734a9206903 Mon Sep 17 00:00:00 2001 From: Oleh Dokuka Date: Fri, 30 Jun 2023 21:18:55 +0300 Subject: [PATCH] adds support for multi-release-jar | rework `Traces.java` Signed-off-by: OlegDokuka --- .github/setup.sh | 13 ++ .github/workflows/ci-mrj.yml | 121 ++++++++++ .github/workflows/ci.yml | 4 +- .sdkmanrc | 3 + build.gradle | 3 + gradle/libs.versions.toml | 2 +- gradle/toolchains.gradle | 109 +++++++++ reactor-core/build.gradle | 46 +--- .../publisher/CallSiteSupplierFactory.java | 169 ++++++++++++++ .../java/reactor/core/publisher/Traces.java | 217 +----------------- .../java8stubs/sun/misc/JavaLangAccess.java | 11 - .../java8stubs/sun/misc/SharedSecrets.java | 9 - .../publisher/CallSiteSupplierFactory.java | 116 ++++++++++ .../java9stubs/java/lang/StackWalker.java | 30 --- .../test/java/reactor/util/LoggerTest.java | 10 +- settings.gradle | 3 +- 16 files changed, 559 insertions(+), 307 deletions(-) create mode 100755 .github/setup.sh create mode 100644 .github/workflows/ci-mrj.yml create mode 100644 .sdkmanrc create mode 100644 gradle/toolchains.gradle create mode 100644 reactor-core/src/main/java/reactor/core/publisher/CallSiteSupplierFactory.java delete mode 100644 reactor-core/src/main/java8stubs/sun/misc/JavaLangAccess.java delete mode 100644 reactor-core/src/main/java8stubs/sun/misc/SharedSecrets.java create mode 100644 reactor-core/src/main/java9/reactor/core/publisher/CallSiteSupplierFactory.java delete mode 100644 reactor-core/src/main/java9stubs/java/lang/StackWalker.java diff --git a/.github/setup.sh b/.github/setup.sh new file mode 100755 index 0000000000..83b729f9a4 --- /dev/null +++ b/.github/setup.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -ex + +########################################################### +# JAVA +########################################################### + +mkdir -p /opt/openjdk +pushd /opt/openjdk > /dev/null +JDK_URL="https://github.com/AdoptOpenJDK/openjdk9-binaries/releases/download/jdk-9.0.4%2B11/OpenJDK9U-jdk_x64_linux_hotspot_9.0.4_11.tar.gz" +mkdir java9 +pushd java9 > /dev/null +curl -L ${JDK_URL} --output OpenJDK9U-jdk_x64_linux_hotspot_9.0.4_11.tar.gz \ No newline at end of file diff --git a/.github/workflows/ci-mrj.yml b/.github/workflows/ci-mrj.yml new file mode 100644 index 0000000000..8f5e671f61 --- /dev/null +++ b/.github/workflows/ci-mrj.yml @@ -0,0 +1,121 @@ +name: CI +on: + pull_request: + branches: + - 3.6.x +permissions: read-all +jobs: + preliminary: + name: preliminary sanity checks + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + with: + fetch-depth: 0 #needed by spotless + - run: ${GITHUB_WORKSPACE}/.github/setup.sh + shell: bash + - uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3 + with: + distribution: 'jdkfile' + java-version: 9.0.4 + jdkFile: /opt/openjdk/java9/OpenJDK9U-jdk_x64_linux_hotspot_9.0.4_11.tar.gz + - uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3 + with: + distribution: 'temurin' + java-version: | + 21-ea + 8 + - uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2 + name: spotless (license header) + if: always() + with: + arguments: spotlessCheck -PspotlessFrom=origin/${{ github.base_ref }} + - uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2 + name: api compatibility + if: always() + with: + arguments: japicmp + - name: how to fix + if: failure() + # the foreground (38;5) color code 208 is orange. we also have bold, white bg (38;5;0;48;5;255m), white fg on black bg... + run: | + echo -e "\n\033[38;5;0;48;5;208m \u001b[1m How to deal with errors in preliminary job: \u001b[0m\033[0m" + echo "(Have a look at the steps above to see what failed exactly)" + echo -e "\n - \u001b[1mSpotless (license headers)\u001b[0m failures on touched java files \033[38;5;255;48;5;0m\u001b[1mcan be automatically fixed by running\u001b[0m:" + echo -e " \033[38;5;0;48;5;255m ./gradlew spotlessApply \033[0m" + echo -e "\n - \u001b[1mAPI Compatibility\u001b[0m failures should be considered carefully and \033[38;5;255;48;5;0m\u001b[1mdiscussed with maintainers in the PR\u001b[0m" + echo " If there are failures, the detail should be available in the step's log:" + echo -e " Look for the \033[38;5;0;48;5;255m API compatibility failures \033[0m block(s)." + echo " Alternatively, locally run the following command to get access to the full report:" + echo -e " \033[38;5;0;48;5;255m ./gradlew japicmp \033[0m" + echo "" + exit -1 + core-fast: + name: core fast tests + runs-on: ubuntu-latest + needs: preliminary + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - run: ${GITHUB_WORKSPACE}/.github/setup.sh + shell: bash + - uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3 + with: + distribution: 'jdkfile' + java-version: 9.0.4 + jdkFile: /opt/openjdk/java9/OpenJDK9U-jdk_x64_linux_hotspot_9.0.4_11.tar.gz + - uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3 + with: + distribution: 'temurin' + java-version: | + 21-ea + 8 + - uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2 + name: gradle + with: + arguments: :reactor-core:test --no-daemon -Pjunit-tags=!slow + core-slow: + name: core slower tests + runs-on: ubuntu-latest + needs: preliminary + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - run: ${GITHUB_WORKSPACE}/.github/setup.sh + shell: bash + - uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3 + with: + distribution: 'jdkfile' + java-version: 9.0.4 + jdkFile: /opt/openjdk/java9/OpenJDK9U-jdk_x64_linux_hotspot_9.0.4_11.tar.gz + - uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3 + with: + distribution: 'temurin' + java-version: | + 21-ea + 8 + - uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2 + name: gradle + with: + arguments: :reactor-core:test --no-daemon -Pjunit-tags=slow + other: + name: other tests + runs-on: ubuntu-latest + needs: preliminary + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - run: ${GITHUB_WORKSPACE}/.github/setup.sh + shell: bash + - uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3 + with: + distribution: 'jdkfile' + java-version: 9.0.4 + jdkFile: /opt/openjdk/java9/OpenJDK9U-jdk_x64_linux_hotspot_9.0.4_11.tar.gz + - uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3 + with: + distribution: 'temurin' + java-version: | + 21-ea + 8 + - uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2 + name: other tests + with: + arguments: check -x :reactor-core:test -x spotlessCheck --no-daemon diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40d1494f5e..3e1961c35d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,8 @@ name: CI on: - pull_request: {} + pull_request: + branches-ignore: + - 3.6.x permissions: read-all jobs: preliminary: diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 0000000000..efa0e43ec4 --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1,3 @@ +# Enable auto-env through the sdkman_auto_env config +# Add key=value pairs of SDKs to use below +java=8.0.372-librca diff --git a/build.gradle b/build.gradle index 32c31b4f43..3d64366c8e 100644 --- a/build.gradle +++ b/build.gradle @@ -38,12 +38,14 @@ plugins { alias(libs.plugins.nohttp) alias(libs.plugins.jcstress) apply false alias(libs.plugins.spotless) + alias(libs.plugins.mrjar) apply false } apply plugin: "io.reactor.gradle.detect-ci" apply from: "gradle/asciidoc.gradle" // asciidoc (which is generated from root dir) apply from: "gradle/releaser.gradle" apply from: "gradle/dependencies.gradle" +apply from: "gradle/toolchains.gradle" repositories { //needed at root for asciidoctor and nohttp-checkstyle mavenCentral() @@ -141,6 +143,7 @@ configure(subprojects) { p -> apply plugin: 'java' apply plugin: 'jacoco' apply from: "${rootDir}/gradle/setup.gradle" + apply from: "${rootDir}/gradle/toolchains.gradle" description = 'Non-Blocking Reactive Foundation for the JVM' group = 'io.projectreactor' diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0f46232f0d..89ef7c194d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,4 +48,4 @@ jcstress = { id = "io.github.reyerizo.gradle.jcstress", version = "0.8.15" } nohttp = { id = "io.spring.nohttp", version = "0.0.11" } shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" } spotless = { id = "com.diffplug.spotless", version = "6.13.0" } - +mrjar = { id = "me.champeau.mrjar", version = "0.1.1" } diff --git a/gradle/toolchains.gradle b/gradle/toolchains.gradle new file mode 100644 index 0000000000..1ff859ffa7 --- /dev/null +++ b/gradle/toolchains.gradle @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2011-2023 VMware Inc. or its affiliates, All Rights Reserved. + * + * Licensed 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 + * + * https://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. + */ + +/** + * Apply the JVM Toolchain conventions + * See https://docs.gradle.org/current/userguide/toolchains.html + * + * One can choose the toolchain to use for compiling and running the TEST sources. + * These options apply to Java, Kotlin and Groovy test sources when available. + * {@code "./gradlew check -PtestToolchain=22"} will use a JDK22 + * toolchain for compiling and running the test SourceSet. + * + * By default, the main build will fall back to using the a JDK 17 + * toolchain (and 17 language level) for all main sourceSets. + * See {@link io.reactor.gradle.JavaConventions}. + * + * Gradle will automatically detect JDK distributions in well-known locations. + * The following command will list the detected JDKs on the host. + * {@code + * $ ./gradlew -q javaToolchains + * } + * + * We can also configure ENV variables and let Gradle know about them: + * {@code + * $ echo JDK17 + * /opt/openjdk/java17 + * $ echo JDK22 + * /opt/openjdk/java22 + * $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK22 check + * } + * + * @author Brian Clozel + * @author Sam Brannen + */ + +def testToolchainConfigured() { + return project.hasProperty('testToolchain') && project.testToolchain +} + +def testToolchainLanguageVersion() { + if (testToolchainConfigured()) { + return JavaLanguageVersion.of(project.testToolchain.toString()) + } + return JavaLanguageVersion.of(8) +} + +plugins.withType(JavaPlugin).configureEach { + // Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined + if (testToolchainConfigured()) { + def testLanguageVersion = testToolchainLanguageVersion() + tasks.withType(JavaCompile).matching { it.name.contains("Test") }.configureEach { + javaCompiler = javaToolchains.compilerFor { + languageVersion = testLanguageVersion + } + } + tasks.withType(Test).configureEach{ + javaLauncher = javaToolchains.launcherFor { + languageVersion = testLanguageVersion + } + } + } +} + +// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode +pluginManager.withPlugin("me.champeau.jmh") { + if (testToolchainConfigured()) { + tasks.matching { it.name.contains('jmh') && it.hasProperty('javaLauncher') }.configureEach { + javaLauncher.set(javaToolchains.launcherFor { + languageVersion.set(testToolchainLanguageVersion()) + }) + } + tasks.withType(JavaCompile).matching { it.name.contains("Jmh") }.configureEach { + javaCompiler = javaToolchains.compilerFor { + languageVersion = testToolchainLanguageVersion() + } + } + } +} + +// Store resolved Toolchain JVM information as custom values in the build scan. +rootProject.ext { + resolvedMainToolchain = false + resolvedTestToolchain = false +} +gradle.taskGraph.afterTask { Task task, TaskState state -> + if (!resolvedMainToolchain && task instanceof JavaCompile && task.javaCompiler.isPresent()) { + def metadata = task.javaCompiler.get().metadata + task.project.buildScan.value('Main toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)") + resolvedMainToolchain = true + } + if (testToolchainConfigured() && !resolvedTestToolchain && task instanceof Test && task.javaLauncher.isPresent()) { + def metadata = task.javaLauncher.get().metadata + task.project.buildScan.value('Test toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)") + resolvedTestToolchain = true + } +} diff --git a/reactor-core/build.gradle b/reactor-core/build.gradle index ad08afa33c..926c4e3a81 100644 --- a/reactor-core/build.gradle +++ b/reactor-core/build.gradle @@ -21,6 +21,7 @@ apply plugin: 'biz.aQute.bnd.builder' apply plugin: 'jvm-test-suite' apply plugin: 'jcstress' apply plugin: 'java-library' +apply plugin: 'me.champeau.mrjar' ext { bndOptions = [ @@ -43,6 +44,10 @@ ext { ] } +multiRelease { + targetVersions 8, 9, 21 +} + testing { suites { test { @@ -347,6 +352,12 @@ if (!JavaVersion.current().isJava9Compatible()) { } } +if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_13)) { + blockHoundTest { + jvmArgs = ["-XX:+AllowRedefinitionToAddDeleteMethods"] + } +} + jar { manifest { attributes 'Implementation-Title': 'reactor-core', @@ -369,39 +380,4 @@ jacocoTestReport.dependsOn test check.dependsOn jacocoTestReport check.dependsOn japicmp - -if (JavaVersion.current().java9Compatible) { - // SharedSecretsCallSiteSupplierFactory is a Java 8 specific optimization. - // It uses sun.misc.SharedSecrets which is unavailable on Java 9+, and the compilation would fail with JDK 9. - // This workaround allows compiling the main sourceset on JDK 9+ while still referring to the Java 8 classes. - - sourceSets { - java8stubs.java.srcDirs = ['src/main/java8stubs'] - } - - tasks.withType(JavaCompile).all { - sourceCompatibility = targetCompatibility = 8 - } - - tasks.withType(Javadoc).all { - excludes = ["reactor/core/publisher/Traces.java"] - } - - dependencies { - compileOnly sourceSets.java8stubs.output - } -} -else { - // reactor.core.publisher.Traces contains a strategy that only works with Java 9+. - // While compiling on Java 8, we can't access Java 9+ APIs. - // To workaround this, we "link" the main sourceset to the stubs of Java 9 APIs without having to use the Java 9 target. - sourceSets { - java9stubs.java.srcDirs = ['src/main/java9stubs'] - } - - dependencies { - compileOnly sourceSets.java9stubs.output - } -} - // docs.zip is added in afterEvaluate block in setup.gradle diff --git a/reactor-core/src/main/java/reactor/core/publisher/CallSiteSupplierFactory.java b/reactor-core/src/main/java/reactor/core/publisher/CallSiteSupplierFactory.java new file mode 100644 index 0000000000..a05b4abc41 --- /dev/null +++ b/reactor-core/src/main/java/reactor/core/publisher/CallSiteSupplierFactory.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2023 VMware Inc. or its affiliates, All Rights Reserved. + * + * Licensed 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 + * + * https://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. + */ + +package reactor.core.publisher; + +import java.util.function.Supplier; +import java.util.stream.Stream; + +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; + +import static reactor.core.publisher.Traces.full; +import static reactor.core.publisher.Traces.isUserCode; +import static reactor.core.publisher.Traces.shouldSanitize; + +class CallSiteSupplierFactory implements Supplier> { + + static final Supplier> supplier; + + static { + String[] strategyClasses = { + CallSiteSupplierFactory.class.getName() + "$SharedSecretsCallSiteSupplierFactory", + CallSiteSupplierFactory.class.getName() + "$ExceptionCallSiteSupplierFactory", + }; + // find one available call-site supplier w.r.t. the jdk version to provide + // linkage-compatibility between jdk 8 and 9+ + supplier = Stream + .of(strategyClasses) + .flatMap(className -> { + try { + Class clazz = Class.forName(className); + @SuppressWarnings("unchecked") + Supplier> function = (Supplier) clazz.getDeclaredConstructor() + .newInstance(); + return Stream.of(function); + } + // explicitly catch LinkageError to support static code analysis + // tools detect the attempt at finding out jdk environment + catch (LinkageError e) { + return Stream.empty(); + } + catch (Throwable e) { + return Stream.empty(); + } + }) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Valid strategy not found")); + } + + + + @Override + public Supplier get() { + return supplier.get(); + } + + @SuppressWarnings("unused") + static class SharedSecretsCallSiteSupplierFactory implements Supplier> { + + static { + SharedSecrets.getJavaLangAccess(); + } + + @Override + public Supplier get() { + return new TracingException(); + } + + static class TracingException extends Throwable implements Supplier { + + static final JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess(); + + @Override + public String get() { + int stackTraceDepth = javaLangAccess.getStackTraceDepth(this); + + StackTraceElement previousElement = null; + // Skip get() + for (int i = 4; i < stackTraceDepth; i++) { + StackTraceElement e = javaLangAccess.getStackTraceElement(this, i); + + String className = e.getClassName(); + if (isUserCode(className)) { + StringBuilder sb = new StringBuilder(); + + if (previousElement != null) { + sb.append("\t").append(previousElement.toString()).append("\n"); + } + sb.append("\t").append(e.toString()).append("\n"); + return sb.toString(); + } + else { + if (!full && e.getLineNumber() <= 1) { + continue; + } + + String classAndMethod = className + "." + e.getMethodName(); + if (!full && shouldSanitize(classAndMethod)) { + continue; + } + previousElement = e; + } + } + + return ""; + } + } + } + + @SuppressWarnings("unused") + static class ExceptionCallSiteSupplierFactory implements Supplier> { + + @Override + public Supplier get() { + return new TracingException(); + } + + static class TracingException extends Throwable implements Supplier { + + @Override + public String get() { + StackTraceElement previousElement = null; + StackTraceElement[] stackTrace = getStackTrace(); + // Skip get() + for (int i = 4; i < stackTrace.length; i++) { + StackTraceElement e = stackTrace[i]; + + String className = e.getClassName(); + if (isUserCode(className)) { + StringBuilder sb = new StringBuilder(); + + if (previousElement != null) { + sb.append("\t").append(previousElement.toString()).append("\n"); + } + sb.append("\t").append(e.toString()).append("\n"); + return sb.toString(); + } + else { + if (!full && e.getLineNumber() <= 1) { + continue; + } + + String classAndMethod = className + "." + e.getMethodName(); + if (!full && shouldSanitize(classAndMethod)) { + continue; + } + previousElement = e; + } + } + + return ""; + } + } + } + +} \ No newline at end of file diff --git a/reactor-core/src/main/java/reactor/core/publisher/Traces.java b/reactor-core/src/main/java/reactor/core/publisher/Traces.java index 7d219562c5..bac53fc126 100644 --- a/reactor-core/src/main/java/reactor/core/publisher/Traces.java +++ b/reactor-core/src/main/java/reactor/core/publisher/Traces.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021 VMware Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2018-2023 VMware Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,11 @@ package reactor.core.publisher; -import java.util.Iterator; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; -import sun.misc.JavaLangAccess; -import sun.misc.SharedSecrets; /** * Utilities around manipulating stack traces and displaying assembly traces. @@ -48,217 +45,7 @@ final class Traces { * each element being prepended with a tabulation and appended with a * newline. */ - static Supplier> callSiteSupplierFactory; - - static { - String[] strategyClasses = { - Traces.class.getName() + "$StackWalkerCallSiteSupplierFactory", - Traces.class.getName() + "$SharedSecretsCallSiteSupplierFactory", - Traces.class.getName() + "$ExceptionCallSiteSupplierFactory", - }; - // find one available call-site supplier w.r.t. the jdk version to provide - // linkage-compatibility between jdk 8 and 9+ - callSiteSupplierFactory = Stream - .of(strategyClasses) - .flatMap(className -> { - try { - Class clazz = Class.forName(className); - @SuppressWarnings("unchecked") - Supplier> function = (Supplier) clazz.getDeclaredConstructor() - .newInstance(); - return Stream.of(function); - } - // explicitly catch LinkageError to support static code analysis - // tools detect the attempt at finding out jdk environment - catch (LinkageError e) { - return Stream.empty(); - } - catch (Throwable e) { - return Stream.empty(); - } - }) - .findFirst() - .orElseThrow(() -> new IllegalStateException("Valid strategy not found")); - } - - /** - * Utility class for the call-site extracting on Java 9+. - * - */ - @SuppressWarnings("unused") - static final class StackWalkerCallSiteSupplierFactory implements Supplier> { - - static { - // Trigger eager StackWalker class loading. - StackWalker.getInstance(); - } - - /** - * Transform the current stack trace into a {@link String} representation, - * each element being prepended with a tabulation and appended with a - * newline. - * - * @return the string version of the stacktrace. - */ - @Override - public Supplier get() { - StackWalker.StackFrame[] stack = StackWalker.getInstance().walk(s -> { - StackWalker.StackFrame[] result = new StackWalker.StackFrame[10]; - Iterator iterator = s.iterator(); - iterator.next(); // .get - - int i = 0; - while (iterator.hasNext()) { - StackWalker.StackFrame frame = iterator.next(); - - if (i >= result.length) { - return new StackWalker.StackFrame[0]; - } - - result[i++] = frame; - - if (isUserCode(frame.getClassName())) { - break; - } - } - StackWalker.StackFrame[] copy = new StackWalker.StackFrame[i]; - System.arraycopy(result, 0, copy, 0, i); - return copy; - }); - - if (stack.length == 0) { - return () -> ""; - } - - if (stack.length == 1) { - return () -> "\t" + stack[0].toString() + "\n"; - } - - return () -> { - StringBuilder sb = new StringBuilder(); - - for (int j = stack.length - 2; j > 0; j--) { - StackWalker.StackFrame previous = stack[j]; - - if (!full) { - if (previous.isNativeMethod()) { - continue; - } - - String previousRow = previous.getClassName() + "." + previous.getMethodName(); - if (shouldSanitize(previousRow)) { - continue; - } - } - sb.append("\t") - .append(previous.toString()) - .append("\n"); - break; - } - - sb.append("\t") - .append(stack[stack.length - 1].toString()) - .append("\n"); - - return sb.toString(); - }; - } - } - - @SuppressWarnings("unused") - static class SharedSecretsCallSiteSupplierFactory implements Supplier> { - - @Override - public Supplier get() { - return new TracingException(); - } - - static class TracingException extends Throwable implements Supplier { - - static final JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess(); - - @Override - public String get() { - int stackTraceDepth = javaLangAccess.getStackTraceDepth(this); - - StackTraceElement previousElement = null; - // Skip get() - for (int i = 2; i < stackTraceDepth; i++) { - StackTraceElement e = javaLangAccess.getStackTraceElement(this, i); - - String className = e.getClassName(); - if (isUserCode(className)) { - StringBuilder sb = new StringBuilder(); - - if (previousElement != null) { - sb.append("\t").append(previousElement.toString()).append("\n"); - } - sb.append("\t").append(e.toString()).append("\n"); - return sb.toString(); - } - else { - if (!full && e.getLineNumber() <= 1) { - continue; - } - - String classAndMethod = className + "." + e.getMethodName(); - if (!full && shouldSanitize(classAndMethod)) { - continue; - } - previousElement = e; - } - } - - return ""; - } - } - } - - @SuppressWarnings("unused") - static class ExceptionCallSiteSupplierFactory implements Supplier> { - - @Override - public Supplier get() { - return new TracingException(); - } - - static class TracingException extends Throwable implements Supplier { - - @Override - public String get() { - StackTraceElement previousElement = null; - StackTraceElement[] stackTrace = getStackTrace(); - // Skip get() - for (int i = 2; i < stackTrace.length; i++) { - StackTraceElement e = stackTrace[i]; - - String className = e.getClassName(); - if (isUserCode(className)) { - StringBuilder sb = new StringBuilder(); - - if (previousElement != null) { - sb.append("\t").append(previousElement.toString()).append("\n"); - } - sb.append("\t").append(e.toString()).append("\n"); - return sb.toString(); - } - else { - if (!full && e.getLineNumber() <= 1) { - continue; - } - - String classAndMethod = className + "." + e.getMethodName(); - if (!full && shouldSanitize(classAndMethod)) { - continue; - } - previousElement = e; - } - } - - return ""; - } - } - } + static final Supplier> callSiteSupplierFactory = new CallSiteSupplierFactory(); /** * Return true for strings (usually from a stack trace element) that should be diff --git a/reactor-core/src/main/java8stubs/sun/misc/JavaLangAccess.java b/reactor-core/src/main/java8stubs/sun/misc/JavaLangAccess.java deleted file mode 100644 index c5e5362b95..0000000000 --- a/reactor-core/src/main/java8stubs/sun/misc/JavaLangAccess.java +++ /dev/null @@ -1,11 +0,0 @@ -package sun.misc; - -/** - * Stub for the Java 8 compatibility when compiled with JDK 9+. - */ -public interface JavaLangAccess { - - int getStackTraceDepth(Throwable e); - - StackTraceElement getStackTraceElement(Throwable e, int depth); -} diff --git a/reactor-core/src/main/java8stubs/sun/misc/SharedSecrets.java b/reactor-core/src/main/java8stubs/sun/misc/SharedSecrets.java deleted file mode 100644 index a49bc02a4d..0000000000 --- a/reactor-core/src/main/java8stubs/sun/misc/SharedSecrets.java +++ /dev/null @@ -1,9 +0,0 @@ -package sun.misc; - -/** - * Stub for the Java 8 compatibility when compiled with JDK 9+. - */ -public class SharedSecrets { - - public static native JavaLangAccess getJavaLangAccess(); -} diff --git a/reactor-core/src/main/java9/reactor/core/publisher/CallSiteSupplierFactory.java b/reactor-core/src/main/java9/reactor/core/publisher/CallSiteSupplierFactory.java new file mode 100644 index 0000000000..d248cda6dc --- /dev/null +++ b/reactor-core/src/main/java9/reactor/core/publisher/CallSiteSupplierFactory.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2023 VMware Inc. or its affiliates, All Rights Reserved. + * + * Licensed 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 + * + * https://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. + */ + +package reactor.core.publisher; + +import java.util.Iterator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static reactor.core.publisher.Traces.full; +import static reactor.core.publisher.Traces.isUserCode; +import static reactor.core.publisher.Traces.shouldSanitize; + +/** + * Utility class for the call-site extracting on Java 9+. + */ +final class CallSiteSupplierFactory implements Supplier>, Function, StackWalker.StackFrame[]> { + + static { + // Trigger eager StackWalker class loading. + StackWalker.getInstance(); + } + + @Override + public StackWalker.StackFrame[] apply(Stream s) { + StackWalker.StackFrame[] result = + new StackWalker.StackFrame[10]; + Iterator iterator = s.iterator(); + iterator.next(); // .get + + int i = 0; + while (iterator.hasNext()) { + StackWalker.StackFrame frame = iterator.next(); + + if (i >= result.length) { + return new StackWalker.StackFrame[0]; + } + + result[i++] = frame; + + if (isUserCode(frame.getClassName())) { + break; + } + } + StackWalker.StackFrame[] copy = + new StackWalker.StackFrame[i]; + System.arraycopy(result, 0, copy, 0, i); + return copy; + } + + /** + * Transform the current stack trace into a {@link String} representation, + * each element being prepended with a tabulation and appended with a + * newline. + * + * @return the string version of the stacktrace. + */ + @Override + public Supplier get() { + StackWalker.StackFrame[] stack = + StackWalker.getInstance() + .walk(this); + + if (stack.length == 0) { + return () -> ""; + } + + if (stack.length == 1) { + return () -> "\t" + stack[0].toString() + "\n"; + } + + return () -> { + StringBuilder sb = new StringBuilder(); + + for (int j = stack.length - 2; j > 0; j--) { + StackWalker.StackFrame previous = stack[j]; + + if (!full) { + if (previous.isNativeMethod()) { + continue; + } + + String previousRow = + previous.getClassName() + "." + previous.getMethodName(); + if (shouldSanitize(previousRow)) { + continue; + } + } + sb.append("\t") + .append(previous.toString()) + .append("\n"); + break; + } + + sb.append("\t") + .append(stack[stack.length - 1].toString()) + .append("\n"); + + return sb.toString(); + }; + } +} \ No newline at end of file diff --git a/reactor-core/src/main/java9stubs/java/lang/StackWalker.java b/reactor-core/src/main/java9stubs/java/lang/StackWalker.java deleted file mode 100644 index 4f2264eceb..0000000000 --- a/reactor-core/src/main/java9stubs/java/lang/StackWalker.java +++ /dev/null @@ -1,30 +0,0 @@ -package java.lang; - -import java.util.function.Function; -import java.util.stream.Stream; - -import sun.reflect.CallerSensitive; - -/** - * Stub for the Java 9 compatibility when compiled with JDK 8. - */ -public class StackWalker { - - public static StackWalker getInstance() { - return null; - } - - @CallerSensitive - public T walk(Function, ? extends T> function) { - return null; - } - - public interface StackFrame { - String getClassName(); - - String getMethodName(); - - boolean isNativeMethod(); - } - -} diff --git a/reactor-core/src/test/java/reactor/util/LoggerTest.java b/reactor-core/src/test/java/reactor/util/LoggerTest.java index c6762b6a77..7d24c4873c 100644 --- a/reactor-core/src/test/java/reactor/util/LoggerTest.java +++ b/reactor-core/src/test/java/reactor/util/LoggerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 VMware Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2021-2023 VMware Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,10 @@ */ class LoggerTest { + static abstract class InnerLogger implements Logger { } + private Logger mockVerbose() { - Logger mockLogger = Mockito.mock(Logger.class); + Logger mockLogger = Mockito.mock(InnerLogger.class); when(mockLogger.isInfoEnabled()).thenReturn(true); when(mockLogger.isWarnEnabled()).thenReturn(true); @@ -46,7 +48,7 @@ private Logger mockVerbose() { } private Logger mockTerse() { - Logger mockLogger = Mockito.mock(Logger.class); + Logger mockLogger = Mockito.mock(InnerLogger.class); when(mockLogger.isInfoEnabled()).thenReturn(true); when(mockLogger.isWarnEnabled()).thenReturn(true); @@ -64,7 +66,7 @@ private Logger mockTerse() { } private Logger mockSilent() { - Logger mockLogger = Mockito.mock(Logger.class); + Logger mockLogger = Mockito.mock(InnerLogger.class); when(mockLogger.isInfoEnabled()).thenReturn(false); when(mockLogger.isWarnEnabled()).thenReturn(false); diff --git a/settings.gradle b/settings.gradle index ee44636416..4f63b37f1b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2022 VMware Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2011-2023 VMware Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ plugins { id "com.gradle.enterprise" version "3.12.4" + id "org.gradle.toolchains.foojay-resolver-convention" version "0.5.0" } rootProject.name = 'reactor'