From ca62e4b4daf56d4091e032271681c5d79eecb288 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Fri, 15 Dec 2023 19:55:35 +0700 Subject: [PATCH] build: updates to build and test with LTS JDKs This removes retrolambda which is incompatible with recent JDKs in favor of performing the release job with JDK 11, which can compile java 1.6. This is the same way as done in zipkin and zipkin-reporter-java. This also skips Spring 2.5 tests on JDK >11 due to cglib problems. It also removes powermock which is not compatible either. Signed-off-by: Adrian Cole --- .github/CONTRIBUTING.md | 2 +- .github/workflows/create_release.yml | 13 +- .github/workflows/deploy.yml | 13 +- .github/workflows/test.yml | 53 ++- .mvn/wrapper/maven-wrapper.jar | Bin 50710 -> 62547 bytes .mvn/wrapper/maven-wrapper.properties | 20 +- RELEASE.md | 16 +- brave-bom/pom.xml | 8 +- brave-tests/pom.xml | 11 - .../test/IntegrationTestSpanHandler.java | 4 +- .../test/IntegrationTestSpanHandlerTest.java | 4 +- brave/pom.xml | 51 ++- brave/src/main/java/brave/Tracing.java | 10 +- .../brave/baggage/BaggagePropagation.java | 24 +- .../baggage/BaggagePropagationConfig.java | 11 +- .../brave/baggage/CorrelationFlushScope.java | 9 +- .../baggage/CorrelationScopeDecorator.java | 8 +- .../main/java/brave/handler/MutableSpan.java | 8 +- .../main/java/brave/internal/Platform.java | 43 +- .../brave/internal/RecyclableBuffers.java | 4 +- .../internal/WrappingExecutorService.java | 4 +- .../brave/internal/baggage/BaggageFields.java | 4 +- .../baggage/SingleFieldBaggageCodec.java | 4 +- .../java/brave/internal/collect/Lists.java | 8 +- .../internal/collect/UnsafeArrayMap.java | 10 +- .../internal/collect/WeakConcurrentMap.java | 6 +- .../java/brave/internal/extra/MapExtra.java | 4 +- .../brave/internal/extra/MapExtraFactory.java | 6 +- .../brave/internal/handler/OrphanTracker.java | 5 +- .../internal/propagation/InjectorFactory.java | 20 +- .../propagation/StringPropagationAdapter.java | 10 +- .../brave/internal/recorder/PendingSpans.java | 7 +- .../brave/internal/recorder/TickClock.java | 9 +- .../java/brave/propagation/B3Propagation.java | 4 +- .../propagation/CurrentTraceContext.java | 25 +- .../propagation/ExtraFieldPropagation.java | 18 +- .../java/brave/propagation/Propagation.java | 4 +- .../StrictCurrentTraceContext.java | 4 +- .../propagation/StrictScopeDecorator.java | 2 +- .../ThreadLocalCurrentTraceContext.java | 4 +- .../brave/propagation/ThreadLocalSpan.java | 7 +- .../TraceContextOrSamplingFlags.java | 4 +- .../brave/sampler/DeclarativeSampler.java | 10 +- .../src/main/java/brave/sampler/Matchers.java | 6 +- .../brave/sampler/ParameterizedSampler.java | 10 +- .../brave/sampler/RateLimitingSampler.java | 13 +- .../java/brave/sampler/SamplerFunctions.java | 4 +- brave/src/test/java/brave/TracerTest.java | 2 +- .../java/brave/internal/PlatformTest.java | 180 ++++---- .../internal/recorder/TickClockTest.java | 30 +- .../brave/propagation/B3PropagationTest.java | 181 ++++---- .../brave/propagation/B3SingleFormatTest.java | 414 +++++++++++------- .../sampler/RateLimitingSamplerTest.java | 279 ++++++------ build-bin/README.md | 129 +----- build-bin/docker/configure_docker | 7 +- build-bin/git/login_git | 2 +- build-bin/git/version_from_trigger_tag | 6 +- build-bin/gpg/configure_gpg | 2 +- build-bin/maven/maven_deploy | 2 +- build-bin/maven/maven_go_offline | 2 +- build-bin/maven/maven_opts | 2 +- build-bin/maven/maven_release | 2 +- build-bin/test | 5 +- context/jfr/pom.xml | 9 - context/log4j12/pom.xml | 38 +- context/log4j2/pom.xml | 38 +- context/rxjava2/pom.xml | 39 +- .../TraceContextCompletableObserver.java | 6 +- .../TraceContextConnectableFlowable.java | 4 +- .../TraceContextConnectableObservable.java | 6 +- .../rxjava2/internal/TraceContextMaybe.java | 4 +- .../internal/TraceContextMaybeObserver.java | 8 +- .../internal/TraceContextObservable.java | 4 +- .../internal/TraceContextObserver.java | 8 +- .../rxjava2/internal/TraceContextSingle.java | 4 +- .../internal/TraceContextSingleObserver.java | 6 +- .../internal/TraceContextSubscriber.java | 8 +- .../context/rxjava2/internal/Wrappers.java | 36 +- context/slf4j/pom.xml | 38 +- instrumentation/benchmarks/pom.xml | 9 - instrumentation/dubbo-rpc/pom.xml | 38 +- .../java/brave/dubbo/rpc/TracingFilter.java | 9 +- .../dubbo/rpc/TracingResponseCallback.java | 10 +- instrumentation/grpc/pom.xml | 39 +- instrumentation/grpc/src/it/grpc12/pom.xml | 4 +- .../main/java/brave/grpc/GrpcPropagation.java | 8 +- .../brave/grpc/TraceContextBinaryFormat.java | 4 +- .../brave/grpc/TracingClientInterceptor.java | 98 +++-- .../brave/grpc/TracingServerInterceptor.java | 106 +++-- instrumentation/http-tests-jakarta/pom.xml | 9 - instrumentation/http-tests/pom.xml | 9 - instrumentation/http/pom.xml | 38 +- .../java/brave/http/HttpClientHandler.java | 12 +- .../brave/http/HttpClientParserAdapter.java | 6 +- .../main/java/brave/http/HttpRuleSampler.java | 4 +- .../src/main/java/brave/http/HttpSampler.java | 4 +- .../java/brave/http/HttpServerHandler.java | 10 +- .../brave/http/HttpServerParserAdapter.java | 6 +- .../src/main/java/brave/http/HttpTags.java | 6 +- .../src/main/java/brave/http/HttpTracing.java | 4 +- instrumentation/httpasyncclient/pom.xml | 38 +- .../TracingHttpAsyncClientBuilder.java | 21 +- instrumentation/httpclient/pom.xml | 40 +- .../brave/httpclient/TracingProtocolExec.java | 20 +- instrumentation/httpclient5/pom.xml | 39 +- instrumentation/jaxrs2/pom.xml | 38 +- instrumentation/jms-jakarta/pom.xml | 2 - instrumentation/jms/pom.xml | 38 +- .../src/main/java/brave/jms/JmsTracing.java | 2 +- .../main/java/brave/jms/PropertyFilter.java | 7 +- .../brave/jms/TracingCompletionListener.java | 10 +- .../brave/jms/TracingExceptionListener.java | 6 +- .../java/brave/jms/TracingJMSProducer.java | 13 +- .../brave/jms/TracingMessageListener.java | 13 +- .../brave/jms/TracingMessageProducer.java | 170 ++++--- instrumentation/kafka-clients/pom.xml | 11 +- instrumentation/kafka-streams/pom.xml | 13 - instrumentation/messaging/pom.xml | 38 +- .../messaging/MessagingRequestMatchers.java | 8 +- .../brave/messaging/MessagingTracing.java | 4 +- instrumentation/mongodb/pom.xml | 39 +- .../mongodb/TraceMongoCommandListener.java | 4 +- instrumentation/mysql/pom.xml | 39 +- instrumentation/netty-codec-http/pom.xml | 39 +- .../netty/http/TracingHttpServerHandler.java | 20 +- instrumentation/okhttp3/pom.xml | 37 ++ instrumentation/p6spy/pom.xml | 38 +- .../java/brave/p6spy/TracingP6SpyOptions.java | 4 +- instrumentation/rpc/pom.xml | 38 +- .../java/brave/rpc/RpcRequestMatchers.java | 6 +- .../src/main/java/brave/rpc/RpcTracing.java | 4 +- instrumentation/servlet-jakarta/pom.xml | 2 - instrumentation/servlet/pom.xml | 40 +- .../java/brave/servlet/TracingFilter.java | 16 +- .../servlet/internal/ServletRuntime.java | 12 +- instrumentation/sparkjava/pom.xml | 13 - instrumentation/spring-rabbit/pom.xml | 38 +- instrumentation/spring-web/pom.xml | 39 +- .../web/TraceContextListenableFuture.java | 26 +- ...cingAsyncClientHttpRequestInterceptor.java | 26 +- .../TracingClientHttpRequestInterceptor.java | 17 +- instrumentation/spring-webmvc/pom.xml | 77 +++- .../brave/spring/webmvc/WebMvcRuntime.java | 4 +- .../spring/webmvc/WebMvcRuntimeTest.java | 52 +-- instrumentation/vertx-web/pom.xml | 14 - mvnw | 224 +++++----- mvnw.cmd | 387 ++++++++-------- pom.xml | 131 +++--- spring-beans/pom.xml | 38 +- .../spring/beans/TracingFactoryBean.java | 4 +- 150 files changed, 2778 insertions(+), 1639 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 643e5e5f77..b95a557b72 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -23,7 +23,7 @@ can be automatically added by running `./mvnw com.mycila:license-maven-plugin:fo ``` /** - * Copyright 2020 The OpenZipkin Authors + * Copyright 2023 The OpenZipkin Authors * * 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 diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index d9d2a387df..3bb12b15a3 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -11,17 +11,22 @@ on: jobs: create_release: - runs-on: ubuntu-20.04 # newest available distribution, aka focal + runs-on: ubuntu-22.04 # newest available distribution, aka jellyfish steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: # Prevent use of implicit GitHub Actions read-only token GITHUB_TOKEN. We don't deploy on # the tag MAJOR.MINOR.PATCH event, but we still need to deploy the maven-release-plugin master commit. token: ${{ secrets.GH_TOKEN }} - fetch-depth: 1 # only need the base commit as license check isn't run + fetch-depth: 1 # only need the HEAD commit as license check isn't run + - name: Setup java + uses: actions/setup-java@v4 + with: + distribution: 'zulu' # zulu as it supports a wide version range + java-version: '11' # earliest LTS and last that can compile the 1.6 release profile. - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index aacfe79d48..e4069ac180 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,17 +14,22 @@ on: jobs: deploy: - runs-on: ubuntu-20.04 # newest available distribution, aka focal + runs-on: ubuntu-22.04 # newest available distribution, aka jellyfish steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: # Prevent use of implicit GitHub Actions read-only token GITHUB_TOKEN. # We push Javadocs to the gh-pages branch on commit. token: ${{ secrets.GH_TOKEN }} - fetch-depth: 0 # allow build-bin/javadoc_to_gh_pages to get the full history + fetch-depth: 0 # allow build-bin/idl_to_gh_pages to get the full history + - name: Setup java + uses: actions/setup-java@v4 + with: + distribution: 'zulu' # zulu as it supports a wide version range + java-version: '11' # earliest LTS and last that can compile the 1.6 release profile. - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 49040c10b0..133923247f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,21 +15,58 @@ on: paths-ignore: '**/*.md' jobs: + test-javadoc: + name: Test JavaDoc Builds + runs-on: ubuntu-22.04 # newest available distribution, aka jellyfish + if: "!contains(github.event.head_commit.message, 'maven-release-plugin')" + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # full git history for license check + - name: Setup java + uses: actions/setup-java@v4 + with: + distribution: 'zulu' # zulu as it supports a wide version range + java-version: '11' # earliest LTS and last that can compile the 1.6 release profile. + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-jdk-11-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-jdk-11-maven- + - name: Build JavaDoc + run: ./mvnw clean javadoc:aggregate -Prelease + test: - runs-on: ubuntu-20.04 # newest available distribution, aka focal + name: test (JDK ${{ matrix.java_version }}) + runs-on: ubuntu-22.04 # newest available distribution, aka jellyfish if: "!contains(github.event.head_commit.message, 'maven-release-plugin')" + strategy: + fail-fast: false # don't fail fast as sometimes failures are operating system specific + matrix: # use latest available versions and be consistent on all workflows! + include: + - java_version: 11 # Last that can compile brave to 1.6 + maven_args: -Prelease -Dgpg.skip -Dmaven.javadoc.skip=true + - java_version: 21 # Most recent LTS steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + fetch-depth: 0 # full git history for license check + - name: Setup java + uses: actions/setup-java@v4 with: - fetch-depth: 0 # full git history for license check + distribution: 'zulu' # zulu as it supports a wide version range + java-version: ${{ matrix.java_version }} - name: Cache local Maven repository - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-maven- - # We can't cache Docker without using buildx because GH actions restricts /var/lib/docker - # That's ok because DOCKER_PARENT_IMAGE is always ghcr.io and local anyway. + key: ${{ runner.os }}-jdk-${{ matrix.java_version }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-jdk-${{ matrix.java_version }}-maven- + # Don't attempt to cache Docker. Sensitive information can be stolen + # via forks, and login session ends up in ~/.docker. This is ok because + # we publish DOCKER_PARENT_IMAGE to ghcr.io, hence local to the runner. - name: Test run: build-bin/configure_test && build-bin/test diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index 2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054..cb28b0e37c7d206feb564310fdeec0927af4123a 100644 GIT binary patch literal 62547 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 A?*IS* literal 50710 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 EfAhh=DF6Tf diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 0c675e6e69..346d645fd0 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,18 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar \ No newline at end of file +# 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. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/RELEASE.md b/RELEASE.md index 84e84c544e..aecf4d753e 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -55,11 +55,19 @@ the following: Before you do the first release of the year, move the SNAPSHOT version back and forth from whatever the current is. In-between, re-apply the licenses. + +Note: the command below is more complex than a normal project because this +project has a bom. ```bash -$ ./mvnw versions:set -DnewVersion=1.3.3-SNAPSHOT -DgenerateBackupPoms=false -$ ./mvnw com.mycila:license-maven-plugin:format -$ ./mvnw versions:set -DnewVersion=1.3.2-SNAPSHOT -DgenerateBackupPoms=false -$ git commit -am"Adjusts copyright headers for this year" +$ mvn=$PWD/mvnw +$ for p in ./bom .; do + (cd $p + $mvn versions:set -DnewVersion=2.17.1-SNAPSHOT -DgenerateBackupPoms=false + $mvn -o clean install -DskipTests + $mvn com.mycila:license-maven-plugin:format + $mvn versions:set -DnewVersion=2.17.0-SNAPSHOT -DgenerateBackupPoms=false) + done +$ git commit -asm"Adjusts copyright headers for this year" ``` ## Manually releasing diff --git a/brave-bom/pom.xml b/brave-bom/pom.xml index 21b273e1ac..f5816d58b9 100644 --- a/brave-bom/pom.xml +++ b/brave-bom/pom.xml @@ -36,8 +36,6 @@ 2.23.2 2.16.3 - - 1.6.8 @@ -315,7 +313,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - ${nexus-staging-maven-plugin.version} + 1.6.13 true ossrh @@ -326,12 +324,12 @@ maven-deploy-plugin - 3.0.0-M1 + 3.1.1 maven-gpg-plugin - 1.6 + 3.1.0 sign-artifacts diff --git a/brave-tests/pom.xml b/brave-tests/pom.xml index ef723de9cc..90bc537a68 100644 --- a/brave-tests/pom.xml +++ b/brave-tests/pom.xml @@ -27,8 +27,6 @@ ${project.basedir}/.. - 1.8 - java18 @@ -61,15 +59,6 @@ - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - diff --git a/brave-tests/src/main/java/brave/test/IntegrationTestSpanHandler.java b/brave-tests/src/main/java/brave/test/IntegrationTestSpanHandler.java index 18ea0a6877..37f6d3a877 100644 --- a/brave-tests/src/main/java/brave/test/IntegrationTestSpanHandler.java +++ b/brave-tests/src/main/java/brave/test/IntegrationTestSpanHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -128,7 +128,7 @@ public final class IntegrationTestSpanHandler extends SpanHandler implements Tes public IntegrationTestSpanHandler() { // OrphanTracker detects to see if it should add "brave.flushed" or not, as it is used in - // production some times and avoiding this could be helpful. This forces a failed match, + // production sometimes and avoiding this could be helpful. This forces a failed match, // so that we can detect orphans even when no data was added. MutableSpan intentionallyWrongDefaultSpan = new MutableSpan(); intentionallyWrongDefaultSpan.tag("not", "me"); diff --git a/brave-tests/src/test/java/brave/test/IntegrationTestSpanHandlerTest.java b/brave-tests/src/test/java/brave/test/IntegrationTestSpanHandlerTest.java index ad248e159c..62699357b8 100644 --- a/brave-tests/src/test/java/brave/test/IntegrationTestSpanHandlerTest.java +++ b/brave-tests/src/test/java/brave/test/IntegrationTestSpanHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -45,7 +45,7 @@ public class IntegrationTestSpanHandlerTest { assertThatThrownBy(spanHandler::takeLocalSpan) .hasMessageStartingWith("Orphaned span found") .hasMessageContaining("brave.flush") - .hasMessageEndingWith("Look for code missing span.flush() or span.finish()."); + .hasMessageContaining("Look for code missing span.flush() or span.finish()."); } @Test public void toString_includesSpans() { diff --git a/brave/pom.xml b/brave/pom.xml index 7d997df5c7..1c20b1875a 100644 --- a/brave/pom.xml +++ b/brave/pom.xml @@ -31,8 +31,6 @@ brave ${project.basedir}/.. - 1.6 - java16 -Xep:EqualsUnsafeCast:OFF @@ -61,19 +59,6 @@ 0.18 test - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito2 - ${powermock.version} - test - io.opentracing opentracing-api @@ -136,4 +121,40 @@ + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/brave/src/main/java/brave/Tracing.java b/brave/src/main/java/brave/Tracing.java index 291015fdc2..cad13e7127 100644 --- a/brave/src/main/java/brave/Tracing.java +++ b/brave/src/main/java/brave/Tracing.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -50,7 +50,7 @@ * for example via spring or when mocking. */ public abstract class Tracing implements Closeable { - static final AtomicReference CURRENT = new AtomicReference<>(); + static final AtomicReference CURRENT = new AtomicReference(); public static Builder newBuilder() { return new Builder(); @@ -148,7 +148,7 @@ public static final class Builder { boolean alwaysSampleLocal = false, alwaysReportSpans = false, trackOrphans = false; Propagation.Factory propagationFactory = B3Propagation.FACTORY; ErrorParser errorParser = new ErrorParser(); - Set spanHandlers = new LinkedHashSet<>(); // dupes not ok + Set spanHandlers = new LinkedHashSet(); // dupes not ok Builder() { defaultSpan.localServiceName("unknown"); @@ -162,7 +162,7 @@ public static final class Builder { * @since 5.12 */ public Set spanHandlers() { - return Collections.unmodifiableSet(new LinkedHashSet<>(spanHandlers)); + return Collections.unmodifiableSet(new LinkedHashSet(spanHandlers)); } /** @@ -480,7 +480,7 @@ static final class Default extends Tracing { defaultSpan.localIp(Platform.get().linkLocalIp()); } - Set spanHandlers = new LinkedHashSet<>(builder.spanHandlers); + Set spanHandlers = new LinkedHashSet(builder.spanHandlers); // When present, the Zipkin handler is invoked after the user-supplied ones. if (builder.zipkinSpanReporter != null) { spanHandlers.add( diff --git a/brave/src/main/java/brave/baggage/BaggagePropagation.java b/brave/src/main/java/brave/baggage/BaggagePropagation.java index 988774545a..5c7d990977 100644 --- a/brave/src/main/java/brave/baggage/BaggagePropagation.java +++ b/brave/src/main/java/brave/baggage/BaggagePropagation.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -99,8 +99,8 @@ public static FactoryBuilder newFactoryBuilder(Propagation.Factory delegate) { public static class FactoryBuilder { // not final to backport ExtraFieldPropagation final Propagation.Factory delegate; - final List extractKeyNames = new ArrayList<>(); - final Set configs = new LinkedHashSet<>(); + final List extractKeyNames = new ArrayList(); + final Set configs = new LinkedHashSet(); FactoryBuilder(Propagation.Factory delegate) { if (delegate == null) throw new NullPointerException("delegate == null"); @@ -115,7 +115,7 @@ public static class FactoryBuilder { // not final to backport ExtraFieldPropagat * @since 5.11 */ public Set configs() { - return Collections.unmodifiableSet(new LinkedHashSet<>(configs)); + return Collections.unmodifiableSet(new LinkedHashSet(configs)); } /** @@ -183,8 +183,8 @@ static final class Factory extends Propagation.Factory implements Propagation fields = new ArrayList<>(); - Set localFieldNames = new LinkedHashSet<>(); + List fields = new ArrayList(); + Set localFieldNames = new LinkedHashSet(); int maxDynamicFields = 0; for (BaggagePropagationConfig config : factoryBuilder.configs) { maxDynamicFields += config.maxDynamicFields; @@ -199,11 +199,11 @@ static final class Factory extends Propagation.Factory implements Propagation BaggagePropagation create(KeyFactory keyFactory) { - return new BaggagePropagation<>(StringPropagationAdapter.create(get(), keyFactory)); + return new BaggagePropagation(StringPropagationAdapter.create(get(), keyFactory)); } @Override public BaggagePropagation get() { - return new BaggagePropagation<>(this); + return new BaggagePropagation(this); } @Override public TraceContext decorate(TraceContext context) { @@ -224,11 +224,11 @@ static final class Factory extends Propagation.Factory implements Propagation Injector injector(Setter setter) { - return new BaggageInjector<>(this, setter); + return new BaggageInjector(this, setter); } @Override public Extractor extractor(Getter getter) { - return new BaggageExtractor<>(this, getter); + return new BaggageExtractor(this, getter); } } @@ -280,14 +280,12 @@ public static List allKeyNames(Propagation propagation) { List baggageKeyNames = getAllKeyNames(emptyExtraction); if (baggageKeyNames.isEmpty()) return propagation.keys(); - List result = new ArrayList<>(propagation.keys().size() + baggageKeyNames.size()); + List result = new ArrayList(propagation.keys().size() + baggageKeyNames.size()); result.addAll(propagation.keys()); result.addAll(baggageKeyNames); return Collections.unmodifiableList(result); } - // Not lambda as Retrolambda creates an OSGi dependency on jdk.internal.vm.annotation with JDK 14 - // See https://github.com/luontola/retrolambda/issues/160 enum NoopGetter implements Getter { INSTANCE; diff --git a/brave/src/main/java/brave/baggage/BaggagePropagationConfig.java b/brave/src/main/java/brave/baggage/BaggagePropagationConfig.java index 81b6ec0433..7c46e1daae 100644 --- a/brave/src/main/java/brave/baggage/BaggagePropagationConfig.java +++ b/brave/src/main/java/brave/baggage/BaggagePropagationConfig.java @@ -99,7 +99,7 @@ public Builder toBuilder() { /** @since 5.11 */ public static final class Builder { final BaggageField field; - List keyNames = new ArrayList<>(); + List keyNames = new ArrayList(); Builder(BaggageField field) { this.field = field; @@ -107,7 +107,7 @@ public static final class Builder { Builder(SingleBaggageField input) { this.field = input.field; - this.keyNames = new ArrayList<>(input.keyNames()); + this.keyNames = new ArrayList(input.keyNames()); } /** @@ -137,8 +137,11 @@ public SingleBaggageField build() { ? BaggageCodec.NOOP : SingleFieldBaggageCodec.single(builder.field, builder.keyNames), 0); field = builder.field; - keyNames = builder.keyNames.isEmpty() ? Collections.emptySet() - : Collections.unmodifiableSet(new LinkedHashSet<>(builder.keyNames)); + if (builder.keyNames.isEmpty()) { + keyNames = Collections.emptySet(); + } else { + keyNames = Collections.unmodifiableSet(new LinkedHashSet(builder.keyNames)); + } } public BaggageField field() { diff --git a/brave/src/main/java/brave/baggage/CorrelationFlushScope.java b/brave/src/main/java/brave/baggage/CorrelationFlushScope.java index 016c5cacfe..e84fb2212b 100644 --- a/brave/src/main/java/brave/baggage/CorrelationFlushScope.java +++ b/brave/src/main/java/brave/baggage/CorrelationFlushScope.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -46,7 +46,7 @@ final class CorrelationFlushScope extends AtomicBoolean implements Scope { * BaggageField#updateValue(String)}. */ static void flush(BaggageField field, String value) { - Set syncedContexts = new LinkedHashSet<>(); + Set syncedContexts = new LinkedHashSet(); for (Object o : updateScopeStack()) { CorrelationUpdateScope next = ((CorrelationUpdateScope) o); String name = next.name(field); @@ -66,12 +66,13 @@ static void flush(BaggageField field, String value) { } } - static final ThreadLocal> updateScopeStack = new ThreadLocal<>(); + static final ThreadLocal> updateScopeStack = + new ThreadLocal>(); static ArrayDeque updateScopeStack() { ArrayDeque stack = updateScopeStack.get(); if (stack == null) { - stack = new ArrayDeque<>(); + stack = new ArrayDeque(); updateScopeStack.set(stack); } return stack; diff --git a/brave/src/main/java/brave/baggage/CorrelationScopeDecorator.java b/brave/src/main/java/brave/baggage/CorrelationScopeDecorator.java index 89b2071a3a..f0bf68e6d4 100644 --- a/brave/src/main/java/brave/baggage/CorrelationScopeDecorator.java +++ b/brave/src/main/java/brave/baggage/CorrelationScopeDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -72,8 +72,8 @@ public abstract class CorrelationScopeDecorator implements ScopeDecorator { public static abstract class Builder { final CorrelationContext context; // Don't allow mixed case of the same name! - final Set allNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - final Set fields = new LinkedHashSet<>(); + final Set allNames = new TreeSet(String.CASE_INSENSITIVE_ORDER); + final Set fields = new LinkedHashSet(); /** Internal constructor used by subtypes. */ protected Builder(CorrelationContext context) { @@ -91,7 +91,7 @@ protected Builder(CorrelationContext context) { * @since 5.11 */ public Set configs() { - return Collections.unmodifiableSet(new LinkedHashSet<>(fields)); + return Collections.unmodifiableSet(new LinkedHashSet(fields)); } /** diff --git a/brave/src/main/java/brave/handler/MutableSpan.java b/brave/src/main/java/brave/handler/MutableSpan.java index 0aa10ec82f..6e6bbeb981 100644 --- a/brave/src/main/java/brave/handler/MutableSpan.java +++ b/brave/src/main/java/brave/handler/MutableSpan.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -609,7 +609,7 @@ public long annotationTimestampAt(int i) { // IndexOutOfBoundsException(i) is Java 9+ if (i < 0) throw new IndexOutOfBoundsException("i < 0"); if (i >= annotationCount) throw new IndexOutOfBoundsException("i >= annotationCount"); - return (long) annotations[i * 2]; + return (Long) annotations[i * 2]; } /** @@ -658,7 +658,7 @@ public Collection> annotations() { */ public void forEachAnnotation(AnnotationConsumer annotationConsumer, T target) { for (int i = 0, length = annotationCount * 2; i < length; i += 2) { - long timestamp = (long) annotations[i]; + long timestamp = (Long) annotations[i]; annotationConsumer.accept(target, timestamp, annotations[i + 1].toString()); } } @@ -682,7 +682,7 @@ public void forEachAnnotation(AnnotationConsumer annotationConsumer, T ta public void forEachAnnotation(AnnotationUpdater annotationUpdater) { for (int i = 0, length = annotationCount * 2; i < length; i += 2) { String value = annotations[i + 1].toString(); - String newValue = annotationUpdater.update((long) annotations[i], value); + String newValue = annotationUpdater.update((Long) annotations[i], value); if (newValue != null) { update(annotations, i, newValue); } else { diff --git a/brave/src/main/java/brave/internal/Platform.java b/brave/src/main/java/brave/internal/Platform.java index b4db6ce97b..2b610997ad 100644 --- a/brave/src/main/java/brave/internal/Platform.java +++ b/brave/src/main/java/brave/internal/Platform.java @@ -34,11 +34,15 @@ * *

Originally designed by OkHttp team, derived from {@code okhttp3.internal.platform.Platform} */ -public abstract class Platform { +public abstract class Platform implements Clock { private static final Platform PLATFORM = findPlatform(); volatile String linkLocalIp; + /** Returns the same value as {@link System#nanoTime()}. */ + @Nullable public abstract long nanoTime(); + + /** Guards {@link InetSocketAddress#getHostString()}, as it isn't available until Java 7 */ @Nullable public abstract String getHostString(InetSocketAddress socket); @@ -112,7 +116,7 @@ static Platform findPlatform() { try { Class zoneId = Class.forName("java.time.ZoneId"); Class.forName("java.time.Clock").getMethod("tickMillis", zoneId); - return new Jre9(); // intentionally doesn't not access the type prior to the above guard + return new Jre9(); // intentionally doesn't access the type prior to the above guard } catch (ClassNotFoundException e) { // pre JRE 8 } catch (NoSuchMethodException e) { @@ -122,7 +126,7 @@ static Platform findPlatform() { // Find JRE 7 new methods try { Class.forName("java.util.concurrent.ThreadLocalRandom"); - return new Jre7(); // intentionally doesn't not access the type prior to the above guard + return new Jre7(); // intentionally doesn't access the type prior to the above guard } catch (ClassNotFoundException e) { // pre JRE 7 } @@ -163,12 +167,16 @@ public Clock clock() { } static class Jre9 extends Jre7 { + @Override public long currentTimeMicroseconds() { + java.time.Instant instant = java.time.Clock.systemUTC().instant(); + return (instant.getEpochSecond() * 1000000) + (instant.getNano() / 1000); + } + @IgnoreJRERequirement @Override public Clock clock() { return new Clock() { // we could use jdk.internal.misc.VM to do this more efficiently, but it is internal @Override public long currentTimeMicroseconds() { - java.time.Instant instant = java.time.Clock.systemUTC().instant(); - return (instant.getEpochSecond() * 1000000) + (instant.getNano() / 1000); + return Jre9.this.currentTimeMicroseconds(); } @Override public String toString() { @@ -183,6 +191,14 @@ static class Jre9 extends Jre7 { } static class Jre7 extends Platform { + @Override public long currentTimeMicroseconds() { + return System.currentTimeMillis() * 1000; + } + + @Override public long nanoTime() { + return System.nanoTime(); + } + @IgnoreJRERequirement @Override public String getHostString(InetSocketAddress socket) { return socket.getHostString(); } @@ -192,7 +208,7 @@ static class Jre7 extends Platform { } @IgnoreJRERequirement @Override public long nextTraceIdHigh() { - return nextTraceIdHigh(java.util.concurrent.ThreadLocalRandom.current().nextInt()); + return nextTraceIdHigh(currentTimeMicroseconds(), java.util.concurrent.ThreadLocalRandom.current().nextInt()); } @IgnoreJRERequirement @Override @@ -205,13 +221,20 @@ public AssertionError assertionError(String message, Throwable cause) { } } - static long nextTraceIdHigh(int random) { - long epochSeconds = System.currentTimeMillis() / 1000; + static long nextTraceIdHigh(long currentTimeMicroseconds, int random) { + long epochSeconds = currentTimeMicroseconds / 1000000; return (epochSeconds & 0xffffffffL) << 32 | (random & 0xffffffffL); } static class Jre6 extends Platform { + @Override public long currentTimeMicroseconds() { + return System.currentTimeMillis() * 1000; + } + + @Override public long nanoTime() { + return System.nanoTime(); + } @Override public String getHostString(InetSocketAddress socket) { return socket.getAddress().getHostAddress(); @@ -222,13 +245,13 @@ static class Jre6 extends Platform { } @Override public long nextTraceIdHigh() { - return nextTraceIdHigh(prng.nextInt()); + return nextTraceIdHigh(currentTimeMicroseconds(), prng.nextInt()); } final Random prng; Jre6() { - this.prng = new Random(System.nanoTime()); + this.prng = new Random(nanoTime()); } @Override public String toString() { diff --git a/brave/src/main/java/brave/internal/RecyclableBuffers.java b/brave/src/main/java/brave/internal/RecyclableBuffers.java index 8bc940773a..9392c474b1 100644 --- a/brave/src/main/java/brave/internal/RecyclableBuffers.java +++ b/brave/src/main/java/brave/internal/RecyclableBuffers.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -15,7 +15,7 @@ public final class RecyclableBuffers { - private static final ThreadLocal PARSE_BUFFER = new ThreadLocal<>(); + private static final ThreadLocal PARSE_BUFFER = new ThreadLocal(); /** * Returns a {@link ThreadLocal} reused {@code char[]} for use when decoding bytes into an ID hex diff --git a/brave/src/main/java/brave/internal/WrappingExecutorService.java b/brave/src/main/java/brave/internal/WrappingExecutorService.java index 4f49641a9b..68c03ba013 100644 --- a/brave/src/main/java/brave/internal/WrappingExecutorService.java +++ b/brave/src/main/java/brave/internal/WrappingExecutorService.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -104,7 +104,7 @@ public Future submit(Runnable task, T result) { } Collection> wrap(Collection> tasks) { - ArrayList> result = new ArrayList<>(tasks.size()); + ArrayList> result = new ArrayList>(tasks.size()); for (Callable task : tasks) { result.add(wrap(task)); } diff --git a/brave/src/main/java/brave/internal/baggage/BaggageFields.java b/brave/src/main/java/brave/internal/baggage/BaggageFields.java index eac0881368..536257cafd 100644 --- a/brave/src/main/java/brave/internal/baggage/BaggageFields.java +++ b/brave/src/main/java/brave/internal/baggage/BaggageFields.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -88,7 +88,7 @@ Object[] state() { * #isDynamic()}. */ public List getAllFields() { - return Collections.unmodifiableList(new ArrayList<>(keySet())); + return Collections.unmodifiableList(new ArrayList(keySet())); } /** Returns a read-only view of the non-null baggage field values */ diff --git a/brave/src/main/java/brave/internal/baggage/SingleFieldBaggageCodec.java b/brave/src/main/java/brave/internal/baggage/SingleFieldBaggageCodec.java index 54e8051918..8918ebe01e 100644 --- a/brave/src/main/java/brave/internal/baggage/SingleFieldBaggageCodec.java +++ b/brave/src/main/java/brave/internal/baggage/SingleFieldBaggageCodec.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -33,7 +33,7 @@ public static SingleFieldBaggageCodec single(BaggageField field, Collection keyNames) { this.field = field; - this.keyNamesList = Collections.unmodifiableList(new ArrayList<>(keyNames)); + this.keyNamesList = Collections.unmodifiableList(new ArrayList(keyNames)); } @Override public List extractKeyNames() { diff --git a/brave/src/main/java/brave/internal/collect/Lists.java b/brave/src/main/java/brave/internal/collect/Lists.java index b75e5d1871..dc85c68904 100644 --- a/brave/src/main/java/brave/internal/collect/Lists.java +++ b/brave/src/main/java/brave/internal/collect/Lists.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -23,7 +23,7 @@ public final class Lists { public static List ensureMutable(List list) { if (list instanceof ArrayList) return list; int size = list.size(); - ArrayList mutable = new ArrayList<>(size); + ArrayList mutable = new ArrayList(size); for (int i = 0; i < size; i++) { mutable.add(list.get(i)); } @@ -36,12 +36,12 @@ public static List ensureImmutable(List list) { if (list.size() == 1) return Collections.singletonList(list.get(0)); if (isImmutable(list)) return list; - return Collections.unmodifiableList(new ArrayList<>(list)); + return Collections.unmodifiableList(new ArrayList(list)); } static boolean isImmutable(List extra) { assert extra.size() > 1; // Handled by caller. - // avoid copying datastructure by trusting certain names. + // avoid copying data structure by trusting certain names. String simpleName = extra.getClass().getSimpleName(); // We don't need to check EMPTY_LIST or SingletonList here since our only caller handles them // without type-checking. diff --git a/brave/src/main/java/brave/internal/collect/UnsafeArrayMap.java b/brave/src/main/java/brave/internal/collect/UnsafeArrayMap.java index 232973d906..7aecf06cb4 100644 --- a/brave/src/main/java/brave/internal/collect/UnsafeArrayMap.java +++ b/brave/src/main/java/brave/internal/collect/UnsafeArrayMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -46,7 +46,7 @@ public interface Mapper { } public static Builder newBuilder() { - return new Builder<>(); + return new Builder(); } public static final class Builder { @@ -94,8 +94,8 @@ public Map build(Object[] array) { } } if (numFiltered == i / 2) return Collections.emptyMap(); - if (keyMapper == null) return new UnsafeArrayMap<>(array, i, filteredBitSet); - return new KeyMapping<>(this, array, i, filteredBitSet); + if (keyMapper == null) return new UnsafeArrayMap(array, i, filteredBitSet); + return new KeyMapping(this, array, i, filteredBitSet); } } @@ -217,7 +217,7 @@ final class ValuesView extends SetView { final class EntrySetView extends SetView> { @Override Map.Entry elementAtArrayIndex(int i) { - return new SimpleImmutableEntry<>(key(i), value(i + 1)); + return new SimpleImmutableEntry(key(i), value(i + 1)); } @Override public boolean contains(Object o) { diff --git a/brave/src/main/java/brave/internal/collect/WeakConcurrentMap.java b/brave/src/main/java/brave/internal/collect/WeakConcurrentMap.java index bd9186bbb2..5233821f88 100644 --- a/brave/src/main/java/brave/internal/collect/WeakConcurrentMap.java +++ b/brave/src/main/java/brave/internal/collect/WeakConcurrentMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -43,7 +43,7 @@ *

See https://github.com/raphw/weak-lock-free */ public class WeakConcurrentMap extends ReferenceQueue implements Iterable> { - final ConcurrentMap, V> target = new ConcurrentHashMap<>(); + final ConcurrentMap, V> target = new ConcurrentHashMap, V>(); @Nullable public V getIfPresent(K key) { if (key == null) throw new NullPointerException("key == null"); @@ -58,7 +58,7 @@ public class WeakConcurrentMap extends ReferenceQueue implements Iterab if (value == null) throw new NullPointerException("value == null"); expungeStaleEntries(); - return target.putIfAbsent(new WeakKey<>(key, this), value); + return target.putIfAbsent(new WeakKey(key, this), value); } /** Removes the entry with the indicated key and returns the old value or {@code null}. */ diff --git a/brave/src/main/java/brave/internal/extra/MapExtra.java b/brave/src/main/java/brave/internal/extra/MapExtra.java index 17157d5df4..6244678d3b 100644 --- a/brave/src/main/java/brave/internal/extra/MapExtra.java +++ b/brave/src/main/java/brave/internal/extra/MapExtra.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -55,7 +55,7 @@ protected boolean isEmpty() { protected Set keySet() { if (!isDynamic()) return factory.initialFieldIndices.keySet(); Object[] state = state(); - Set result = new LinkedHashSet<>(state.length / 2); + Set result = new LinkedHashSet(state.length / 2); for (int i = 0; i < state.length; i += 2) { result.add((K) state[i]); } diff --git a/brave/src/main/java/brave/internal/extra/MapExtraFactory.java b/brave/src/main/java/brave/internal/extra/MapExtraFactory.java index 731c970f4b..5bd9df1e78 100644 --- a/brave/src/main/java/brave/internal/extra/MapExtraFactory.java +++ b/brave/src/main/java/brave/internal/extra/MapExtraFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -29,7 +29,7 @@ public abstract class MapExtraFactory, public static abstract class Builder, F extends MapExtraFactory, B extends Builder> { - List initialState = new ArrayList<>(); + List initialState = new ArrayList(); int maxDynamicEntries; public final B addInitialKey(K key) { @@ -56,7 +56,7 @@ public final B maxDynamicEntries(int maxDynamicEntries) { protected MapExtraFactory(Builder builder) { super(builder.initialState.toArray()); - Map initialFieldIndices = new LinkedHashMap<>(); + Map initialFieldIndices = new LinkedHashMap(); Object[] initialStateArray = (Object[]) initialState; this.initialArrayLength = initialStateArray.length; for (int i = 0; i < initialArrayLength; i += 2) { diff --git a/brave/src/main/java/brave/internal/handler/OrphanTracker.java b/brave/src/main/java/brave/internal/handler/OrphanTracker.java index f338b68f31..23c4740434 100644 --- a/brave/src/main/java/brave/internal/handler/OrphanTracker.java +++ b/brave/src/main/java/brave/internal/handler/OrphanTracker.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -87,7 +87,8 @@ public SpanHandler build() { final MutableSpan defaultSpan; final Clock clock; - final WeakConcurrentMap spanToCaller = new WeakConcurrentMap<>(); + final WeakConcurrentMap spanToCaller = + new WeakConcurrentMap(); final Level logLevel; OrphanTracker(Builder builder) { diff --git a/brave/src/main/java/brave/internal/propagation/InjectorFactory.java b/brave/src/main/java/brave/internal/propagation/InjectorFactory.java index 916a8836ca..4b9803bb2b 100644 --- a/brave/src/main/java/brave/internal/propagation/InjectorFactory.java +++ b/brave/src/main/java/brave/internal/propagation/InjectorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -152,13 +152,13 @@ public InjectorFactory build() { clientInjectorFunction = builder.clientInjectorFunction; producerInjectorFunction = builder.producerInjectorFunction; consumerInjectorFunction = builder.consumerInjectorFunction; - Set keyNames = new LinkedHashSet<>(); + Set keyNames = new LinkedHashSet(); // Add messaging first as their formats are likely the cheapest to extract keyNames.addAll(builder.consumerInjectorFunction.keyNames()); keyNames.addAll(builder.producerInjectorFunction.keyNames()); keyNames.addAll(builder.clientInjectorFunction.keyNames()); keyNames.addAll(builder.injectorFunction.keyNames()); - this.keyNames = Collections.unmodifiableList(new ArrayList<>(keyNames)); + this.keyNames = Collections.unmodifiableList(new ArrayList(keyNames)); } /** @@ -181,15 +181,15 @@ public TraceContext.Injector newInjector(Setter setter) { RemoteSetter remoteSetter = (RemoteSetter) setter; switch (remoteSetter.spanKind()) { case CLIENT: - return new RemoteInjector<>(setter, clientInjectorFunction); + return new RemoteInjector(setter, clientInjectorFunction); case PRODUCER: - return new RemoteInjector<>(setter, producerInjectorFunction); + return new RemoteInjector(setter, producerInjectorFunction); case CONSUMER: - return new RemoteInjector<>(setter, consumerInjectorFunction); + return new RemoteInjector(setter, consumerInjectorFunction); default: // SERVER is nonsense as it cannot be injected } } - return new DeferredInjector<>(setter, this); + return new DeferredInjector(setter, this); } @Override public int hashCode() { @@ -305,7 +305,7 @@ static final class RemoteInjector implements TraceContext.Injector { static InjectorFunction injectorFunction(InjectorFunction existing, InjectorFunction... update) { if (update == null) throw new NullPointerException("injectorFunctions == null"); LinkedHashSet injectorFunctionSet = - new LinkedHashSet<>(Arrays.asList(update)); + new LinkedHashSet(Arrays.asList(update)); if (injectorFunctionSet.contains(null)) { throw new NullPointerException("injectorFunction == null"); } @@ -321,11 +321,11 @@ static final class CompositeInjectorFunction implements InjectorFunction { CompositeInjectorFunction(InjectorFunction[] injectorFunctions) { this.injectorFunctions = injectorFunctions; - Set keyNames = new LinkedHashSet<>(); + Set keyNames = new LinkedHashSet(); for (InjectorFunction injectorFunction : injectorFunctions) { keyNames.addAll(injectorFunction.keyNames()); } - this.keyNames = Collections.unmodifiableList(new ArrayList<>(keyNames)); + this.keyNames = Collections.unmodifiableList(new ArrayList(keyNames)); } @Override public List keyNames() { diff --git a/brave/src/main/java/brave/internal/propagation/StringPropagationAdapter.java b/brave/src/main/java/brave/internal/propagation/StringPropagationAdapter.java index be08e2464e..0d090db150 100644 --- a/brave/src/main/java/brave/internal/propagation/StringPropagationAdapter.java +++ b/brave/src/main/java/brave/internal/propagation/StringPropagationAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -76,7 +76,7 @@ public static Propagation create(Propagation delegate, KeyFactory if (delegate == null) throw new NullPointerException("delegate == null"); if (keyFactory == null) throw new NullPointerException("keyFactory == null"); if (keyFactory == KeyFactory.STRING) return (Propagation) delegate; - return new StringPropagationAdapter<>(delegate, keyFactory); + return new StringPropagationAdapter(delegate, keyFactory); } final Propagation delegate; @@ -87,7 +87,7 @@ public static Propagation create(Propagation delegate, KeyFactory StringPropagationAdapter(Propagation delegate, KeyFactory keyFactory) { this.delegate = delegate; this.keyFactory = keyFactory; - this.map = new LinkedHashMap<>(); + this.map = new LinkedHashMap(); this.keysList = toKeyList(delegate.keys(), keyFactory); } @@ -110,13 +110,13 @@ List toKeyList(List keyNames, KeyFactory keyFactory) { @Override public Injector injector(Setter setter) { // No check if Setter is a RemoteSetter because this instance cannot have String keys while // RemoteSetter must have String keys - return delegate.injector(new SetterAdapter<>(setter, map)); + return delegate.injector(new SetterAdapter(setter, map)); } @Override public Extractor extractor(Getter getter) { // No check if Setter is a RemoteGetter because this instance cannot have String keys while // RemoteGetter must have String keys - return delegate.extractor(new GetterAdapter<>(getter, map)); + return delegate.extractor(new GetterAdapter(getter, map)); } @Override public int hashCode() { diff --git a/brave/src/main/java/brave/internal/recorder/PendingSpans.java b/brave/src/main/java/brave/internal/recorder/PendingSpans.java index c13a9ed592..c76ba82d5c 100644 --- a/brave/src/main/java/brave/internal/recorder/PendingSpans.java +++ b/brave/src/main/java/brave/internal/recorder/PendingSpans.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -19,6 +19,7 @@ import brave.handler.SpanHandler; import brave.handler.SpanHandler.Cause; import brave.internal.Nullable; +import brave.internal.Platform; import brave.internal.collect.WeakConcurrentMap; import brave.propagation.TraceContext; import java.lang.ref.Reference; @@ -36,12 +37,14 @@ */ public final class PendingSpans extends WeakConcurrentMap { final MutableSpan defaultSpan; + final Platform platform; final Clock clock; final SpanHandler spanHandler; final AtomicBoolean noop; public PendingSpans(MutableSpan defaultSpan, Clock clock, SpanHandler spanHandler, AtomicBoolean noop) { + this.platform = Platform.get(); this.defaultSpan = defaultSpan; this.clock = clock; this.spanHandler = spanHandler; @@ -76,7 +79,7 @@ public PendingSpan getOrCreate( if (start) span.startTimestamp(clock.currentTimeMicroseconds()); } else { long currentTimeMicroseconds = this.clock.currentTimeMicroseconds(); - clock = new TickClock(currentTimeMicroseconds, System.nanoTime()); + clock = new TickClock(platform, currentTimeMicroseconds, platform.nanoTime()); if (start) span.startTimestamp(currentTimeMicroseconds); } diff --git a/brave/src/main/java/brave/internal/recorder/TickClock.java b/brave/src/main/java/brave/internal/recorder/TickClock.java index 9b08a9a4fc..b6c0ad31ca 100644 --- a/brave/src/main/java/brave/internal/recorder/TickClock.java +++ b/brave/src/main/java/brave/internal/recorder/TickClock.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -14,18 +14,21 @@ package brave.internal.recorder; import brave.Clock; +import brave.internal.Platform; final class TickClock implements Clock { + final Platform platform; final long baseEpochMicros; final long baseTickNanos; - TickClock(long baseEpochMicros, long baseTickNanos) { + TickClock(Platform platform, long baseEpochMicros, long baseTickNanos) { + this.platform = platform; this.baseEpochMicros = baseEpochMicros; this.baseTickNanos = baseTickNanos; } @Override public long currentTimeMicroseconds() { - return ((System.nanoTime() - baseTickNanos) / 1000) + baseEpochMicros; + return ((platform.nanoTime() - baseTickNanos) / 1000) + baseEpochMicros; } @Override public String toString() { diff --git a/brave/src/main/java/brave/propagation/B3Propagation.java b/brave/src/main/java/brave/propagation/B3Propagation.java index 751f6ae63a..04ad6a5262 100644 --- a/brave/src/main/java/brave/propagation/B3Propagation.java +++ b/brave/src/main/java/brave/propagation/B3Propagation.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -236,7 +236,7 @@ static final class Factory extends Propagation.Factory implements Propagation Extractor extractor(Getter getter) { if (getter == null) throw new NullPointerException("getter == null"); - return new B3Extractor<>(this, getter); + return new B3Extractor(this, getter); } @Override public int hashCode() { diff --git a/brave/src/main/java/brave/propagation/CurrentTraceContext.java b/brave/src/main/java/brave/propagation/CurrentTraceContext.java index 6bb8265ede..2baef9ab62 100644 --- a/brave/src/main/java/brave/propagation/CurrentTraceContext.java +++ b/brave/src/main/java/brave/propagation/CurrentTraceContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -42,7 +42,7 @@ public abstract class CurrentTraceContext { /** Implementations of this allow standardized configuration, for example scope decoration. */ public abstract static class Builder { - ArrayList scopeDecorators = new ArrayList<>(); + ArrayList scopeDecorators = new ArrayList(); /** * Implementations call decorators in order to add features like log correlation to a scope. @@ -217,7 +217,8 @@ public interface ScopeDecorator { */ public static final class Default extends ThreadLocalCurrentTraceContext { // Inheritable as Brave 3's ThreadLocalServerClientAndLocalSpanState was inheritable - static final InheritableThreadLocal INHERITABLE = new InheritableThreadLocal<>(); + static final InheritableThreadLocal INHERITABLE = + new InheritableThreadLocal(); /** Uses a non-inheritable static thread local */ public static CurrentTraceContext create() { @@ -243,12 +244,15 @@ public static CurrentTraceContext inheritable() { } /** Wraps the input so that it executes with the same context as now. */ - public Callable wrap(Callable task) { + public Callable wrap(final Callable task) { final TraceContext invocationContext = get(); class CurrentTraceContextCallable implements Callable { @Override public C call() throws Exception { - try (Scope scope = maybeScope(invocationContext)) { + Scope scope = maybeScope(invocationContext); + try { return task.call(); + } finally { + scope.close(); } } } @@ -256,12 +260,15 @@ class CurrentTraceContextCallable implements Callable { } /** Wraps the input so that it executes with the same context as now. */ - public Runnable wrap(Runnable task) { + public Runnable wrap(final Runnable task) { final TraceContext invocationContext = get(); class CurrentTraceContextRunnable implements Runnable { @Override public void run() { - try (Scope scope = maybeScope(invocationContext)) { + Scope scope = maybeScope(invocationContext); + try { task.run(); + } finally { + scope.close(); } } } @@ -272,7 +279,7 @@ class CurrentTraceContextRunnable implements Runnable { * Decorates the input such that the {@link #get() current trace context} at the time a task is * scheduled is made current when the task is executed. */ - public Executor executor(Executor delegate) { + public Executor executor(final Executor delegate) { class CurrentTraceContextExecutor implements Executor { @Override public void execute(Runnable task) { delegate.execute(CurrentTraceContext.this.wrap(task)); @@ -285,7 +292,7 @@ class CurrentTraceContextExecutor implements Executor { * Decorates the input such that the {@link #get() current trace context} at the time a task is * scheduled is made current when the task is executed. */ - public ExecutorService executorService(ExecutorService delegate) { + public ExecutorService executorService(final ExecutorService delegate) { class CurrentTraceContextExecutorService extends brave.internal.WrappingExecutorService { @Override protected ExecutorService delegate() { diff --git a/brave/src/main/java/brave/propagation/ExtraFieldPropagation.java b/brave/src/main/java/brave/propagation/ExtraFieldPropagation.java index dd7e28c505..64d411c2e6 100644 --- a/brave/src/main/java/brave/propagation/ExtraFieldPropagation.java +++ b/brave/src/main/java/brave/propagation/ExtraFieldPropagation.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -61,9 +61,9 @@ @Deprecated public static final class FactoryBuilder { final Propagation.Factory delegate; final BaggagePropagation.FactoryBuilder baggageFactory; - // Updates could be out-of-order in the old impl so we track everything until build() - final Set redactedNames = new LinkedHashSet<>(); - final Map> nameToKeyNames = new LinkedHashMap<>(); + // Updates could be out-of-order in the old impl, so we track everything until build() + final Set redactedNames = new LinkedHashSet(); + final Map> nameToKeyNames = new LinkedHashMap>(); FactoryBuilder(Propagation.Factory delegate) { this.delegate = delegate; @@ -74,7 +74,7 @@ @Deprecated public FactoryBuilder addRedactedField(String fieldName) { fieldName = validateFieldName(fieldName); redactedNames.add(fieldName); - nameToKeyNames.put(fieldName, Collections.emptySet()); + nameToKeyNames.put(fieldName, Collections.emptySet()); return this; } @@ -99,13 +99,13 @@ void addKeyName(String name, String keyName) { Set keyNames = nameToKeyNames.get(name); - if (keyNames == null) nameToKeyNames.put(name, keyNames = new LinkedHashSet<>()); + if (keyNames == null) nameToKeyNames.put(name, keyNames = new LinkedHashSet()); keyNames.add(keyName); } /** Returns a wrapper of the delegate if there are no fields to propagate. */ public Factory build() { - Set extraKeyNames = new LinkedHashSet<>(); + Set extraKeyNames = new LinkedHashSet(); for (Map.Entry> entry : nameToKeyNames.entrySet()) { BaggageField field = BaggageField.create(entry.getKey()); if (redactedNames.contains(field.name())) { @@ -205,9 +205,9 @@ public static class Factory extends Propagation.Factory { /** {@inheritDoc} */ @Deprecated @Override public ExtraFieldPropagation create(Propagation.KeyFactory keyFactory) { - List extraKeys = new ArrayList<>(); + List extraKeys = new ArrayList(); for (String extraKeyName : extraKeyNames) extraKeys.add(keyFactory.create(extraKeyName)); - return new ExtraFieldPropagation<>(delegate.create(keyFactory), unmodifiableList(extraKeys)); + return new ExtraFieldPropagation(delegate.create(keyFactory), unmodifiableList(extraKeys)); } @Override public boolean supportsJoin() { diff --git a/brave/src/main/java/brave/propagation/Propagation.java b/brave/src/main/java/brave/propagation/Propagation.java index c770a0798b..5cc76041ae 100644 --- a/brave/src/main/java/brave/propagation/Propagation.java +++ b/brave/src/main/java/brave/propagation/Propagation.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -130,7 +130,7 @@ public TraceContext decorate(TraceContext context) { */ @Deprecated interface KeyFactory { - KeyFactory STRING = new KeyFactory() { // retrolambda no likey + KeyFactory STRING = new KeyFactory() { @Override public String create(String name) { return name; } diff --git a/brave/src/main/java/brave/propagation/StrictCurrentTraceContext.java b/brave/src/main/java/brave/propagation/StrictCurrentTraceContext.java index bff62ffe03..7e08514f5d 100644 --- a/brave/src/main/java/brave/propagation/StrictCurrentTraceContext.java +++ b/brave/src/main/java/brave/propagation/StrictCurrentTraceContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -38,7 +38,7 @@ public static Builder newBuilder() { public static final class Builder extends CurrentTraceContext.Builder { // intentionally not inheritable to ensure instrumentation propagation doesn't accidentally work // intentionally not static to make explicit when instrumentation need per thread semantics - final ThreadLocal local = new ThreadLocal<>(); + final ThreadLocal local = new ThreadLocal(); CurrentTraceContext delegate = new ThreadLocalCurrentTraceContext.Builder(local).build(); StrictScopeDecorator strictScopeDecorator = new StrictScopeDecorator(); diff --git a/brave/src/main/java/brave/propagation/StrictScopeDecorator.java b/brave/src/main/java/brave/propagation/StrictScopeDecorator.java index c18d7968b0..68d68767aa 100644 --- a/brave/src/main/java/brave/propagation/StrictScopeDecorator.java +++ b/brave/src/main/java/brave/propagation/StrictScopeDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 diff --git a/brave/src/main/java/brave/propagation/ThreadLocalCurrentTraceContext.java b/brave/src/main/java/brave/propagation/ThreadLocalCurrentTraceContext.java index 441cba9408..28b91bbb85 100644 --- a/brave/src/main/java/brave/propagation/ThreadLocalCurrentTraceContext.java +++ b/brave/src/main/java/brave/propagation/ThreadLocalCurrentTraceContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -74,7 +74,7 @@ public static final class Builder extends CurrentTraceContext.Builder { } } - static final ThreadLocal DEFAULT = new ThreadLocal<>(); + static final ThreadLocal DEFAULT = new ThreadLocal(); @SuppressWarnings("ThreadLocalUsage") // intentional: to support multiple Tracer instances final ThreadLocal local; diff --git a/brave/src/main/java/brave/propagation/ThreadLocalSpan.java b/brave/src/main/java/brave/propagation/ThreadLocalSpan.java index 782f9c5dbe..4b74657104 100644 --- a/brave/src/main/java/brave/propagation/ThreadLocalSpan.java +++ b/brave/src/main/java/brave/propagation/ThreadLocalSpan.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -171,12 +171,13 @@ static final class SpanAndScope { * not possible because there is no api to place an arbitrary span in scope using this api. */ @SuppressWarnings("ThreadLocalUsage") // intentional: to support multiple Tracer instances - final ThreadLocal> currentSpanInScopeStack = new ThreadLocal<>(); + final ThreadLocal> currentSpanInScopeStack = + new ThreadLocal>(); ArrayDeque getCurrentSpanInScopeStack() { ArrayDeque stack = currentSpanInScopeStack.get(); if (stack == null) { - stack = new ArrayDeque<>(); + stack = new ArrayDeque(); currentSpanInScopeStack.set(stack); } return stack; diff --git a/brave/src/main/java/brave/propagation/TraceContextOrSamplingFlags.java b/brave/src/main/java/brave/propagation/TraceContextOrSamplingFlags.java index da56a1def6..b2316c08db 100644 --- a/brave/src/main/java/brave/propagation/TraceContextOrSamplingFlags.java +++ b/brave/src/main/java/brave/propagation/TraceContextOrSamplingFlags.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -362,7 +362,7 @@ public Builder sampledLocal() { /** @deprecated Since 5.12, use {@link #addExtra(Object)} */ @Deprecated public Builder extra(List extraList) { if (extraList == null) throw new NullPointerException("extraList == null"); - this.extraList = new ArrayList<>(); + this.extraList = new ArrayList(); for (Object extra : extraList) addExtra(extra); return this; } diff --git a/brave/src/main/java/brave/sampler/DeclarativeSampler.java b/brave/src/main/java/brave/sampler/DeclarativeSampler.java index b938cbfeac..249da0afb1 100644 --- a/brave/src/main/java/brave/sampler/DeclarativeSampler.java +++ b/brave/src/main/java/brave/sampler/DeclarativeSampler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -61,17 +61,17 @@ public interface RateOfMethod { public static DeclarativeSampler createWithProbability( ProbabilityOfMethod probabilityOfMethod) { if (probabilityOfMethod == null) throw new NullPointerException("probabilityOfMethod == null"); - return new DeclarativeCountingSampler<>(probabilityOfMethod); + return new DeclarativeCountingSampler(probabilityOfMethod); } /* @since 5.8 */ public static DeclarativeSampler createWithRate(RateOfMethod rateOfMethod) { if (rateOfMethod == null) throw new NullPointerException("rateOfMethod == null"); - return new DeclarativeRateLimitingSampler<>(rateOfMethod); + return new DeclarativeRateLimitingSampler(rateOfMethod); } // this assumes input are compared by identity as typically annotations do not override hashCode - final ConcurrentMap methodToSamplers = new ConcurrentHashMap<>(); + final ConcurrentMap methodToSamplers = new ConcurrentHashMap(); /** * {@inheritDoc} @@ -170,7 +170,7 @@ public interface RateForMethod extends ProbabilityOfMethod { * @since 4.19 * @deprecated Since 5.8, use {@link Tracer#startScopedSpan(String, SamplerFunction, Object)} */ - @Deprecated public Sampler toSampler(M method, Sampler fallback) { + @Deprecated public Sampler toSampler(final M method, final Sampler fallback) { if (fallback == null) throw new NullPointerException("fallback == null"); if (method == null) return fallback; return new Sampler() { diff --git a/brave/src/main/java/brave/sampler/Matchers.java b/brave/src/main/java/brave/sampler/Matchers.java index 66f6b679e7..b43343f9c7 100644 --- a/brave/src/main/java/brave/sampler/Matchers.java +++ b/brave/src/main/java/brave/sampler/Matchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -83,7 +83,7 @@ static

Matcher[] toArray(Iterable> matchers) { if (matchers instanceof Collection) { return (Matcher[]) ((Collection) matchers).toArray(new Matcher[0]); } - List> result = new ArrayList<>(); + List> result = new ArrayList>(); for (Matcher

matcher : matchers) result.add(matcher); return result.toArray(new Matcher[0]); } @@ -95,7 +95,7 @@ static

Matcher

composite(Matcher

[] matchers, boolean and) { if (matchers[i] == null) throw new NullPointerException("matchers[" + i + "] == null"); } if (matchers.length == 1) return matchers[0]; - return and ? new And<>(matchers) : new Or<>(matchers); + return and ? new And

(matchers) : new Or

(matchers); } static class And

implements Matcher

{ diff --git a/brave/src/main/java/brave/sampler/ParameterizedSampler.java b/brave/src/main/java/brave/sampler/ParameterizedSampler.java index e41742fce6..0206adb540 100644 --- a/brave/src/main/java/brave/sampler/ParameterizedSampler.java +++ b/brave/src/main/java/brave/sampler/ParameterizedSampler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -36,12 +36,12 @@ public final class ParameterizedSampler

implements SamplerFunction

{ /** @since 5.8 */ public static

Builder

newBuilder() { - return new Builder<>(); + return new Builder

(); } /** @since 5.8 */ public static final class Builder

{ - final Map, Sampler> rules = new LinkedHashMap<>(); + final Map, Sampler> rules = new LinkedHashMap, Sampler>(); /** * Adds or replaces all rules in this sampler with those of the input. @@ -67,7 +67,7 @@ public Builder

putRule(Matcher

matcher, Sampler sampler) { } public ParameterizedSampler

build() { - return new ParameterizedSampler<>(this); + return new ParameterizedSampler

(this); } Builder() { @@ -90,7 +90,7 @@ static class R

{ this.rules = new R[builder.rules.size()]; int i = 0; for (Map.Entry, Sampler> rule : builder.rules.entrySet()) { - rules[i++] = new R<>(rule.getKey(), rule.getValue()); + rules[i++] = new R

(rule.getKey(), rule.getValue()); } } diff --git a/brave/src/main/java/brave/sampler/RateLimitingSampler.java b/brave/src/main/java/brave/sampler/RateLimitingSampler.java index d6876ebff5..dd70d42ac8 100644 --- a/brave/src/main/java/brave/sampler/RateLimitingSampler.java +++ b/brave/src/main/java/brave/sampler/RateLimitingSampler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -13,6 +13,7 @@ */ package brave.sampler; +import brave.internal.Platform; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -46,25 +47,27 @@ public class RateLimitingSampler extends Sampler { public static Sampler create(int tracesPerSecond) { if (tracesPerSecond < 0) throw new IllegalArgumentException("tracesPerSecond < 0"); if (tracesPerSecond == 0) return Sampler.NEVER_SAMPLE; - return new RateLimitingSampler(tracesPerSecond); + return new RateLimitingSampler(Platform.get(), tracesPerSecond); } static final long NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1); static final long NANOS_PER_DECISECOND = NANOS_PER_SECOND / 10; + final Platform platform; final MaxFunction maxFunction; final AtomicInteger usage = new AtomicInteger(0); final AtomicLong nextReset; - RateLimitingSampler(int tracesPerSecond) { + RateLimitingSampler(Platform platform, int tracesPerSecond) { + this.platform = platform; this.maxFunction = tracesPerSecond < 10 ? new LessThan10(tracesPerSecond) : new AtLeast10(tracesPerSecond); - long now = System.nanoTime(); + long now = platform.nanoTime(); this.nextReset = new AtomicLong(now + NANOS_PER_SECOND); } @Override public boolean isSampled(long ignoredTraceId) { - long now = System.nanoTime(), updateAt = nextReset.get(); + long now = platform.nanoTime(), updateAt = nextReset.get(); // First task is to determine if this request is later than the one second sampling window long nanosUntilReset = -(now - updateAt); // because nanoTime can be negative diff --git a/brave/src/main/java/brave/sampler/SamplerFunctions.java b/brave/src/main/java/brave/sampler/SamplerFunctions.java index fed02d251d..e8b1e7d84a 100644 --- a/brave/src/main/java/brave/sampler/SamplerFunctions.java +++ b/brave/src/main/java/brave/sampler/SamplerFunctions.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -30,7 +30,7 @@ public final class SamplerFunctions { public static SamplerFunction nullSafe(SamplerFunction delegate) { if (delegate == null) throw new NullPointerException("delegate == null"); if (delegate instanceof Constants || delegate instanceof NullSafe) return delegate; - return new NullSafe<>(delegate); + return new NullSafe(delegate); } static final class NullSafe implements SamplerFunction { diff --git a/brave/src/test/java/brave/TracerTest.java b/brave/src/test/java/brave/TracerTest.java index 098a0359c8..239f8eb0d8 100644 --- a/brave/src/test/java/brave/TracerTest.java +++ b/brave/src/test/java/brave/TracerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 diff --git a/brave/src/test/java/brave/internal/PlatformTest.java b/brave/src/test/java/brave/internal/PlatformTest.java index 23daf32cf7..9f9d6e6f86 100644 --- a/brave/src/test/java/brave/internal/PlatformTest.java +++ b/brave/src/test/java/brave/internal/PlatformTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -20,6 +20,7 @@ import java.net.NetworkInterface; import java.net.SocketException; import java.util.ArrayList; +import java.util.Enumeration; import java.util.List; import java.util.Set; import java.util.Vector; @@ -27,145 +28,157 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; import static org.assertj.core.api.Assertions.assertThat; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; - -@RunWith(PowerMockRunner.class) -// Added to declutter console: tells power mock not to mess with implicit classes we aren't testing -@PowerMockIgnore({"org.apache.logging.*", "javax.script.*"}) -@PrepareForTest({Platform.class, NetworkInterface.class}) -public class PlatformTest { - Platform platform = new Platform.Jre7(); - - @Test public void clock_hasNiceToString_jre7() { - assertThat(platform.clock()) - .hasToString("System.currentTimeMillis()"); +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PlatformTest { + @Test void clock_hasNiceToString_jre7() { + Platform platform = new Platform.Jre7(); + + assertThat(platform.clock()).hasToString("System.currentTimeMillis()"); } - @Test public void clock_hasNiceToString_jre9() { + @Test void clock_hasNiceToString_jre9() { Platform platform = new Platform.Jre9(); - assertThat(platform.clock()) - .hasToString("Clock.systemUTC().instant()"); + assertThat(platform.clock()).hasToString("Clock.systemUTC().instant()"); } // example from X-Amzn-Trace-Id: Root=1-5759e988-bd862e3fe1be46a994272793;Sampled=1 - @Test public void randomLong_epochSecondsPlusRandom() { - mockStatic(System.class); - when(System.currentTimeMillis()) - .thenReturn(1465510280_000L); // Thursday, June 9, 2016 10:11:20 PM + @Test void randomLong_epochSecondsPlusRandom() { + Platform platform = new Platform.Jre7() { + @Override public long currentTimeMicroseconds() { + return 1465510280000000L; // Thursday, June 9, 2016 10:11:20 PM + } + }; long traceIdHigh = platform.nextTraceIdHigh(); assertThat(HexCodec.toLowerHex(traceIdHigh)).startsWith("5759e988"); } - @Test public void randomLong_whenRandomIsMostNegative() { - mockStatic(System.class); - when(System.currentTimeMillis()).thenReturn(1465510280_000L); + @Test void randomLong_whenRandomIsMostNegative() { + long traceIdHigh = Platform.nextTraceIdHigh(1465510280000000L, 0xffffffff); - long traceIdHigh = Platform.nextTraceIdHigh(0xffffffff); - - assertThat(HexCodec.toLowerHex(traceIdHigh)) - .isEqualTo("5759e988ffffffff"); + assertThat(HexCodec.toLowerHex(traceIdHigh)).isEqualTo("5759e988ffffffff"); } - @Test public void linkLocalIp_lazySet() { + @Test void linkLocalIp_lazySet() { + Platform platform = Platform.findPlatform(); // not get as it caches nics. assertThat(platform.linkLocalIp).isNull(); // sanity check setup // cannot test as the there is no link local IP if (platform.produceLinkLocalIp() == null) return; - assertThat(platform.linkLocalIp()) - .isNotNull(); + assertThat(platform.linkLocalIp()).isNotNull(); } - @Test public void linkLocalIp_sameInstance() { - assertThat(platform.linkLocalIp()) - .isSameAs(platform.linkLocalIp()); + @Test void linkLocalIp_sameInstance() { + Platform platform = new Platform.Jre7(); + + assertThat(platform.linkLocalIp()).isSameAs(platform.linkLocalIp()); } - @Test public void produceLinkLocalIp_exceptionReadingNics() throws Exception { - mockStatic(NetworkInterface.class); - when(NetworkInterface.getNetworkInterfaces()).thenThrow(SocketException.class); + @Test void produceLinkLocalIp_exceptionReadingNics() { + try (MockedStatic mb = mockStatic(NetworkInterface.class)) { + mb.when(NetworkInterface::getNetworkInterfaces).thenThrow(SocketException.class); - assertThat(platform.produceLinkLocalIp()) - .isNull(); + Platform platform = Platform.findPlatform(); // not get as it caches nics. + assertThat(platform.produceLinkLocalIp()).isNull(); + } } /** possible albeit very unlikely */ - @Test public void produceLinkLocalIp_noNics() throws Exception { - mockStatic(NetworkInterface.class); - - when(NetworkInterface.getNetworkInterfaces()) - .thenReturn(null); + @Test void produceLinkLocalIp_noNics() { + try (MockedStatic mb = mockStatic(NetworkInterface.class)) { + mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(null); - assertThat(platform.linkLocalIp()) - .isNull(); + Platform platform = Platform.findPlatform(); // not get as it caches nics. + assertThat(platform.linkLocalIp()).isNull(); - when(NetworkInterface.getNetworkInterfaces()) - .thenReturn(new Vector().elements()); + mb.when(NetworkInterface::getNetworkInterfaces) + .thenReturn(new Vector().elements()); - assertThat(platform.produceLinkLocalIp()) - .isNull(); + assertThat(platform.produceLinkLocalIp()).isNull(); + } } /** also possible albeit unlikely */ - @Test public void produceLinkLocalIp_noAddresses() throws Exception { - nicWithAddress(null); + @Test void produceLinkLocalIp_noAddresses() { + try (MockedStatic mb = mockStatic(NetworkInterface.class)) { + Enumeration nics = nicsWithAddress(null); + mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(nics); - assertThat(platform.produceLinkLocalIp()) - .isNull(); + Platform platform = Platform.findPlatform(); // not get as it caches nics. + assertThat(platform.produceLinkLocalIp()).isNull(); + } } - @Test public void produceLinkLocalIp_siteLocal_ipv4() throws Exception { - nicWithAddress(InetAddress.getByAddress("local", new byte[] {(byte) 192, (byte) 168, 0, 1})); + @Test void produceLinkLocalIp_siteLocal_ipv4() throws Exception { + try (MockedStatic mb = mockStatic(NetworkInterface.class)) { + InetAddress local = + InetAddress.getByAddress("local", new byte[] {(byte) 192, (byte) 168, 0, 1}); + Enumeration nics = nicsWithAddress(local); + mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(nics); - assertThat(platform.produceLinkLocalIp()) - .isEqualTo("192.168.0.1"); + Platform platform = Platform.findPlatform(); // not get as it caches nics. + assertThat(platform.produceLinkLocalIp()).isEqualTo("192.168.0.1"); + } } - @Test public void produceLinkLocalIp_siteLocal_ipv6() throws Exception { - InetAddress ipv6 = Inet6Address.getByName("fec0:db8::c001"); - nicWithAddress(ipv6); + @Test void produceLinkLocalIp_siteLocal_ipv6() throws Exception { + try (MockedStatic mb = mockStatic(NetworkInterface.class)) { + InetAddress ipv6 = Inet6Address.getByName("fec0:db8::c001"); + Enumeration nics = nicsWithAddress(ipv6); + mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(nics); - assertThat(platform.produceLinkLocalIp()) - .isEqualTo(ipv6.getHostAddress()); + Platform platform = Platform.findPlatform(); // not get as it caches nics. + assertThat(platform.produceLinkLocalIp()).isEqualTo(ipv6.getHostAddress()); + } } - @Test public void produceLinkLocalIp_notSiteLocal_ipv4() throws Exception { - nicWithAddress(InetAddress.getByAddress("external", new byte[] {1, 2, 3, 4})); + @Test void produceLinkLocalIp_notSiteLocal_ipv4() throws Exception { + try (MockedStatic mb = mockStatic(NetworkInterface.class)) { + InetAddress external = InetAddress.getByAddress("external", new byte[] {1, 2, 3, 4}); + Enumeration nics = nicsWithAddress(external); + mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(nics); - assertThat(platform.produceLinkLocalIp()) - .isNull(); + Platform platform = Platform.findPlatform(); // not get as it caches nics. + assertThat(platform.produceLinkLocalIp()).isNull(); + } } - @Test public void produceLinkLocalIp_notSiteLocal_ipv6() throws Exception { - nicWithAddress(Inet6Address.getByName("2001:db8::c001")); + @Test void produceLinkLocalIp_notSiteLocal_ipv6() throws Exception { + try (MockedStatic mb = mockStatic(NetworkInterface.class)) { + InetAddress addr = Inet6Address.getByName("2001:db8::c001"); + Enumeration nics = nicsWithAddress(addr); + mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(nics); - assertThat(platform.produceLinkLocalIp()) - .isNull(); + Platform platform = Platform.findPlatform(); // not get as it caches nics. + assertThat(platform.produceLinkLocalIp()).isNull(); + } } /** * Getting an endpoint is expensive. This tests it is provisioned only once. - * + *

* test inspired by dagger.internal.DoubleCheckTest */ - @Test - public void linkLocalIp_provisionsOnce() throws Exception { + @Test void linkLocalIp_provisionsOnce() throws Exception { + Platform platform = Platform.findPlatform(); // not get as it caches nics. + // create all the tasks up front so that they are executed with no delay List> tasks = new ArrayList<>(); for (int i = 0; i < 10; i++) { - tasks.add(() -> platform.linkLocalIp()); + tasks.add(platform::linkLocalIp); } ExecutorService executor = Executors.newFixedThreadPool(tasks.size()); @@ -181,14 +194,13 @@ public void linkLocalIp_provisionsOnce() throws Exception { executor.shutdownNow(); } - static void nicWithAddress(@Nullable InetAddress address) throws SocketException { - mockStatic(NetworkInterface.class); + static Enumeration nicsWithAddress(@Nullable InetAddress address) { Vector addresses = new Vector<>(); if (address != null) addresses.add(address); NetworkInterface nic = mock(NetworkInterface.class); Vector nics = new Vector<>(); nics.add(nic); - when(NetworkInterface.getNetworkInterfaces()).thenReturn(nics.elements()); when(nic.getInetAddresses()).thenReturn(addresses.elements()); + return nics.elements(); } } diff --git a/brave/src/test/java/brave/internal/recorder/TickClockTest.java b/brave/src/test/java/brave/internal/recorder/TickClockTest.java index b09bbde59f..094d01607c 100644 --- a/brave/src/test/java/brave/internal/recorder/TickClockTest.java +++ b/brave/src/test/java/brave/internal/recorder/TickClockTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -13,27 +13,23 @@ */ package brave.internal.recorder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import brave.internal.Platform; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import static org.assertj.core.api.Assertions.assertThat; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; +import static org.mockito.Mockito.when; -@RunWith(PowerMockRunner.class) -// Added to declutter console: tells power mock not to mess with implicit classes we aren't testing -@PowerMockIgnore({"org.apache.logging.*", "javax.script.*"}) -@PrepareForTest(TickClock.class) -public class TickClockTest { +@ExtendWith(MockitoExtension.class) +class TickClockTest { + @Mock Platform platform; - @Test public void relativeTimestamp_incrementsAccordingToNanoTick() { - mockStatic(System.class); - TickClock clock = new TickClock(1000L /* 1ms */, 0L /* 0ns */); + @Test void relativeTimestamp_incrementsAccordingToNanoTick() { + TickClock clock = new TickClock(platform, 1000L /* 1ms */, 0L /* 0ns */); - when(System.nanoTime()).thenReturn(1000L); // 1 microsecond = 1000 nanoseconds + when(platform.nanoTime()).thenReturn(1000L); // 1 microsecond = 1000 nanoseconds assertThat(clock.currentTimeMicroseconds()).isEqualTo(1001L); // 1ms + 1us } diff --git a/brave/src/test/java/brave/propagation/B3PropagationTest.java b/brave/src/test/java/brave/propagation/B3PropagationTest.java index ea0dbcbd0b..c8844ad85d 100644 --- a/brave/src/test/java/brave/propagation/B3PropagationTest.java +++ b/brave/src/test/java/brave/propagation/B3PropagationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -23,27 +23,22 @@ import java.util.Map; import java.util.stream.Stream; import org.assertj.core.api.InstanceOfAssertFactories; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; - -@RunWith(PowerMockRunner.class) -// Added to declutter console: tells power mock not to mess with implicit classes we aren't testing -@PowerMockIgnore({"org.apache.logging.*", "javax.script.*"}) -@PrepareForTest({Platform.class, B3Propagation.class}) -public class B3PropagationTest { + +@ExtendWith(MockitoExtension.class) +class B3PropagationTest { String traceIdHigh = "0000000000000009"; String traceId = "0000000000000001"; String parentId = "0000000000000002"; @@ -52,34 +47,24 @@ public class B3PropagationTest { TraceContext context = TraceContext.newBuilder().traceId(1).parentId(2).spanId(3).build(); Propagation propagation = B3Propagation.B3_STRING; - Platform platform = mock(Platform.class); - - @Before public void setupLogger() { - mockStatic(Platform.class); - when(Platform.get()).thenReturn(platform); + @Mock Platform platform; + + @Test void keys_defaultToAll() { + propagation = B3Propagation.newFactoryBuilder() + .build().get(); + + assertThat(propagation.keys()).containsExactly( + "b3", + "X-B3-TraceId", + "X-B3-SpanId", + "X-B3-ParentSpanId", + "X-B3-Sampled", + "X-B3-Flags" + ); } - /** Either we asserted on the log messages or there weren't any */ - @After public void ensureNothingLogged() { - verifyNoMoreInteractions(platform); - } - - @Test public void keys_defaultToAll() { - propagation = B3Propagation.newFactoryBuilder() - .build().get(); - - assertThat(propagation.keys()).containsExactly( - "b3", - "X-B3-TraceId", - "X-B3-SpanId", - "X-B3-ParentSpanId", - "X-B3-Sampled", - "X-B3-Flags" - ); - } - - @Test public void keys_withoutB3Single() { - propagation = B3Propagation.newFactoryBuilder() + @Test void keys_withoutB3Single() { + propagation = B3Propagation.newFactoryBuilder() .injectFormat(Span.Kind.PRODUCER, Format.MULTI) .injectFormat(Span.Kind.CONSUMER, Format.MULTI) .build().get(); @@ -93,7 +78,7 @@ public class B3PropagationTest { ); } - @Test public void keys_onlyB3Single() { + @Test void keys_onlyB3Single() { propagation = B3Propagation.newFactoryBuilder() .injectFormat(Format.SINGLE) .injectFormat(Span.Kind.CLIENT, Format.SINGLE) @@ -103,7 +88,7 @@ public class B3PropagationTest { assertThat(propagation.keys()).containsOnly("b3"); } - @Test public void injectFormat() { + @Test void injectFormat() { B3Propagation.Factory factory = (B3Propagation.Factory) B3Propagation.newFactoryBuilder() .injectFormat(Format.SINGLE) .build(); @@ -112,7 +97,7 @@ public class B3PropagationTest { .isEqualTo(Format.SINGLE); } - @Test public void injectKindFormat() { + @Test void injectKindFormat() { B3Propagation.Factory factory = (B3Propagation.Factory) B3Propagation.newFactoryBuilder() .injectFormat(Span.Kind.CLIENT, Format.SINGLE) .build(); @@ -121,7 +106,7 @@ public class B3PropagationTest { .isEqualTo(Format.SINGLE); } - @Test public void injectKindFormats() { + @Test void injectKindFormats() { B3Propagation.Factory factory = (B3Propagation.Factory) B3Propagation.newFactoryBuilder() .injectFormats(Span.Kind.CLIENT, Format.SINGLE, Format.MULTI) .build(); @@ -131,13 +116,13 @@ public class B3PropagationTest { .containsExactly(Format.SINGLE, Format.MULTI); } - @Test public void injectKindFormats_cantBeSame() { + @Test void injectKindFormats_cantBeSame() { assertThatThrownBy(() -> B3Propagation.newFactoryBuilder() .injectFormats(Span.Kind.CLIENT, Format.MULTI, Format.MULTI)) .isInstanceOf(IllegalArgumentException.class); } - @Test public void injectKindFormats_cantBeBothSingle() { + @Test void injectKindFormats_cantBeBothSingle() { assertThatThrownBy(() -> B3Propagation.newFactoryBuilder() .injectFormats(Span.Kind.CLIENT, Format.SINGLE, Format.SINGLE_NO_PARENT)) .isInstanceOf(IllegalArgumentException.class); @@ -159,7 +144,7 @@ void header(String key, String value) { } } - @Test public void clientUsesB3Multi() { + @Test void clientUsesB3Multi() { ClientRequest request = new ClientRequest(); Propagation.B3_STRING.injector(ClientRequest::header).inject(context, request); @@ -186,7 +171,7 @@ void header(String key, String value) { } } - @Test public void producerUsesB3SingleNoParent_deferred() { + @Test void producerUsesB3SingleNoParent_deferred() { // This injector won't know the type it is injecting until the call to inject() Injector injector = Propagation.B3_STRING.injector(ProducerRequest::header); @@ -208,7 +193,7 @@ static class ProducerSetter implements RemoteSetter { } } - @Test public void producerUsesB3SingleNoParent() { + @Test void producerUsesB3SingleNoParent() { // This injector needs no instanceof checks during inject() Injector injector = Propagation.B3_STRING.injector(new ProducerSetter()); @@ -220,7 +205,7 @@ static class ProducerSetter implements RemoteSetter { .containsEntry("b3", "0000000000000001-0000000000000003"); } - @Test public void canConfigureSingle() { + @Test void canConfigureSingle() { propagation = B3Propagation.newFactoryBuilder() .injectFormat(Format.SINGLE_NO_PARENT) .build().get(); @@ -233,7 +218,7 @@ static class ProducerSetter implements RemoteSetter { .containsEntry("b3", "0000000000000001-0000000000000003"); } - @Test public void canConfigureBasedOnKind() { + @Test void canConfigureBasedOnKind() { propagation = B3Propagation.newFactoryBuilder() .injectFormats(Span.Kind.CLIENT, Format.SINGLE, Format.MULTI) .build().get(); @@ -249,7 +234,7 @@ static class ProducerSetter implements RemoteSetter { .containsEntry("b3", traceId + "-" + spanId + "-" + parentId); } - @Test public void extract_notYetSampled() { + @Test void extract_notYetSampled() { Map headers = new LinkedHashMap<>(); headers.put("X-B3-TraceId", traceId); headers.put("X-B3-SpanId", spanId); @@ -257,7 +242,7 @@ static class ProducerSetter implements RemoteSetter { assertThat(extract(headers).sampled()).isNull(); } - @Test public void extract_sampled() { + @Test void extract_sampled() { Map headers = new LinkedHashMap<>(); headers.put("X-B3-TraceId", traceId); headers.put("X-B3-SpanId", spanId); @@ -271,7 +256,7 @@ static class ProducerSetter implements RemoteSetter { assertThat(extract(headers).sampled()).isTrue(); } - @Test public void extract_128Bit() { + @Test void extract_128Bit() { Map headers = new LinkedHashMap<>(); headers.put("X-B3-TraceId", traceIdHigh + traceId); headers.put("X-B3-SpanId", spanId); @@ -283,7 +268,7 @@ static class ProducerSetter implements RemoteSetter { ); } - @Test public void extract_padded() { + @Test void extract_padded() { Map headers = new LinkedHashMap<>(); headers.put("X-B3-TraceId", "0000000000000000" + traceId); headers.put("X-B3-SpanId", spanId); @@ -294,7 +279,7 @@ static class ProducerSetter implements RemoteSetter { ); } - @Test public void extract_padded_right() { + @Test void extract_padded_right() { Map headers = new LinkedHashMap<>(); headers.put("X-B3-TraceId", traceIdHigh + "0000000000000000"); headers.put("X-B3-SpanId", spanId); @@ -305,37 +290,49 @@ static class ProducerSetter implements RemoteSetter { ); } - @Test public void extract_zeros_traceId() { - Map headers = new LinkedHashMap<>(); - headers.put("X-B3-TraceId", "0000000000000000"); - headers.put("X-B3-SpanId", spanId); + @Test void extract_zeros_traceId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + Map headers = new LinkedHashMap<>(); + headers.put("X-B3-TraceId", "0000000000000000"); + headers.put("X-B3-SpanId", spanId); - assertThat(extract(headers).context()).isNull(); + assertThat(extract(headers).context()).isNull(); - verify(platform).log("Invalid input: traceId was all zeros", null); + verify(platform).log("Invalid input: traceId was all zeros", null); + } } - @Test public void extract_zeros_traceId_128() { - Map headers = new LinkedHashMap<>(); - headers.put("X-B3-TraceId", "00000000000000000000000000000000"); - headers.put("X-B3-SpanId", spanId); + @Test void extract_zeros_traceId_128() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + Map headers = new LinkedHashMap<>(); + headers.put("X-B3-TraceId", "00000000000000000000000000000000"); + headers.put("X-B3-SpanId", spanId); - assertThat(extract(headers).context()).isNull(); + assertThat(extract(headers).context()).isNull(); - verify(platform).log("Invalid input: traceId was all zeros", null); + verify(platform).log("Invalid input: traceId was all zeros", null); + } } - @Test public void extract_zeros_spanId() { - Map headers = new LinkedHashMap<>(); - headers.put("X-B3-TraceId", traceId); - headers.put("X-B3-SpanId", "0000000000000000"); + @Test void extract_zeros_spanId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + Map headers = new LinkedHashMap<>(); + headers.put("X-B3-TraceId", traceId); + headers.put("X-B3-SpanId", "0000000000000000"); - assertThat(extract(headers).context()).isNull(); + assertThat(extract(headers).context()).isNull(); - verify(platform).log("Invalid input: spanId was all zeros", null); + verify(platform).log("Invalid input: spanId was all zeros", null); + } } - @Test public void extract_sampled_false() { + @Test void extract_sampled_false() { Map headers = new LinkedHashMap<>(); headers.put("X-B3-TraceId", traceId); headers.put("X-B3-SpanId", spanId); @@ -349,26 +346,30 @@ static class ProducerSetter implements RemoteSetter { assertThat(extract(headers).sampled()).isFalse(); } - @Test public void extract_sampledCorrupt() { - Map headers = new LinkedHashMap<>(); - headers.put("X-B3-TraceId", traceId); - headers.put("X-B3-SpanId", spanId); + @Test void extract_sampledCorrupt() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - Stream.of("", "d", "💩", "hello").forEach(sampled -> { - headers.put("X-B3-Sampled", sampled); - assertThat(extract(headers)).isSameAs(TraceContextOrSamplingFlags.EMPTY); + Map headers = new LinkedHashMap<>(); + headers.put("X-B3-TraceId", traceId); + headers.put("X-B3-SpanId", spanId); - verify(platform).log("Invalid input: expected 0 or 1 for X-B3-Sampled, but found '{0}'", - sampled, null); - }); + Stream.of("", "d", "💩", "hello").forEach(sampled -> { + headers.put("X-B3-Sampled", sampled); + assertThat(extract(headers)).isSameAs(TraceContextOrSamplingFlags.EMPTY); + + verify(platform).log("Invalid input: expected 0 or 1 for X-B3-Sampled, but found '{0}'", + sampled, null); + }); + } } - @Test public void build_defaultIsSingleton() { + @Test void build_defaultIsSingleton() { assertThat(B3Propagation.newFactoryBuilder().build()) .isSameAs(B3Propagation.FACTORY); } - @Test public void equalsAndHashCode() { + @Test void equalsAndHashCode() { // same instance are equivalent Propagation.Factory factory = B3Propagation.newFactoryBuilder() .injectFormat(Span.Kind.CLIENT, Format.SINGLE_NO_PARENT) diff --git a/brave/src/test/java/brave/propagation/B3SingleFormatTest.java b/brave/src/test/java/brave/propagation/B3SingleFormatTest.java index 3aad2f2c80..9815e60f58 100644 --- a/brave/src/test/java/brave/propagation/B3SingleFormatTest.java +++ b/brave/src/test/java/brave/propagation/B3SingleFormatTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -14,13 +14,11 @@ package brave.propagation; import brave.internal.Platform; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; import static brave.propagation.B3SingleFormat.parseB3SingleFormat; import static brave.propagation.B3SingleFormat.writeB3SingleFormat; @@ -29,35 +27,25 @@ import static brave.propagation.B3SingleFormat.writeB3SingleFormatWithoutParentIdAsBytes; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; - -@RunWith(PowerMockRunner.class) -// Added to declutter console: tells power mock not to mess with implicit classes we aren't testing -@PowerMockIgnore({"org.apache.logging.*", "javax.script.*"}) -@PrepareForTest({Platform.class, B3SingleFormat.class}) -public class B3SingleFormatTest { + +@ExtendWith(MockitoExtension.class) +class B3SingleFormatTest { String traceIdHigh = "1234567890123459"; String traceId = "1234567890123451"; String parentId = "1234567890123452"; String spanId = "1234567890123453"; - Platform platform = mock(Platform.class); - @Before public void setupLogger() { - mockStatic(Platform.class); - when(Platform.get()).thenReturn(platform); - } - /** Either we asserted on the log messages or there weren't any */ - @After public void ensureNothingLogged() { + @AfterEach void ensureNothingLogged() { verifyNoMoreInteractions(platform); } - @Test public void writeB3SingleFormat_notYetSampled() { + @Test void writeB3SingleFormat_notYetSampled() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .spanId(Long.parseUnsignedLong(spanId, 16)).build(); @@ -67,7 +55,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormat_notYetSampled_128() { + @Test void writeB3SingleFormat_notYetSampled_128() { TraceContext context = TraceContext.newBuilder() .traceIdHigh(Long.parseUnsignedLong(traceIdHigh, 16)) .traceId(Long.parseUnsignedLong(traceId, 16)) @@ -78,7 +66,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormat_unsampled() { + @Test void writeB3SingleFormat_unsampled() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .spanId(Long.parseUnsignedLong(spanId, 16)) @@ -89,7 +77,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormat_sampled() { + @Test void writeB3SingleFormat_sampled() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .spanId(Long.parseUnsignedLong(spanId, 16)) @@ -100,7 +88,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormat_debug() { + @Test void writeB3SingleFormat_debug() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .spanId(Long.parseUnsignedLong(spanId, 16)) @@ -111,7 +99,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormat_parent() { + @Test void writeB3SingleFormat_parent() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .parentId(Long.parseUnsignedLong(parentId, 16)) @@ -123,7 +111,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormat_largest() { + @Test void writeB3SingleFormat_largest() { TraceContext context = TraceContext.newBuilder() .traceIdHigh(Long.parseUnsignedLong(traceIdHigh, 16)) .traceId(Long.parseUnsignedLong(traceId, 16)) @@ -136,7 +124,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatAsBytes(context), UTF_8)); } - @Test public void parseB3SingleFormat_largest() { + @Test void parseB3SingleFormat_largest() { assertThat( parseB3SingleFormat(traceIdHigh + traceId + "-" + spanId + "-1-" + parentId).context() ).isEqualToComparingFieldByField(TraceContext.newBuilder() @@ -148,7 +136,7 @@ public class B3SingleFormatTest { ); } - @Test public void parseB3SingleFormat_padded() { + @Test void parseB3SingleFormat_padded() { assertThat( parseB3SingleFormat("0000000000000000" + traceId + "-" + spanId + "-1-" + parentId).context() ).isEqualToComparingFieldByField(TraceContext.newBuilder() @@ -159,7 +147,7 @@ public class B3SingleFormatTest { ); } - @Test public void parseTraceparentFormat_padded_right() { + @Test void parseTraceparentFormat_padded_right() { assertThat( parseB3SingleFormat(traceIdHigh + "0000000000000000-" + spanId + "-1-" + parentId).context() ).isEqualToComparingFieldByField(TraceContext.newBuilder() @@ -170,7 +158,7 @@ public class B3SingleFormatTest { ); } - @Test public void writeB3SingleFormatWithoutParent_notYetSampled() { + @Test void writeB3SingleFormatWithoutParent_notYetSampled() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .spanId(Long.parseUnsignedLong(spanId, 16)).build(); @@ -180,7 +168,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatWithoutParentIdAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormatWithoutParent_unsampled() { + @Test void writeB3SingleFormatWithoutParent_unsampled() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .parentId(Long.parseUnsignedLong(parentId, 16)) @@ -192,7 +180,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatWithoutParentIdAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormatWithoutParent_sampled() { + @Test void writeB3SingleFormatWithoutParent_sampled() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .parentId(Long.parseUnsignedLong(parentId, 16)) @@ -204,7 +192,7 @@ public class B3SingleFormatTest { .isEqualTo(new String(writeB3SingleFormatWithoutParentIdAsBytes(context), UTF_8)); } - @Test public void writeB3SingleFormatWithoutParent_debug() { + @Test void writeB3SingleFormatWithoutParent_debug() { TraceContext context = TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) .parentId(Long.parseUnsignedLong(parentId, 16)) @@ -217,7 +205,7 @@ public class B3SingleFormatTest { } /** for example, parsing a w3c context */ - @Test public void parseB3SingleFormat_middleOfString() { + @Test void parseB3SingleFormat_middleOfString() { String input = "b3=" + traceIdHigh + traceId + "-" + spanId + ","; assertThat(parseB3SingleFormat(input, 3, input.length() - 1).context()) .isEqualToComparingFieldByField(TraceContext.newBuilder() @@ -228,22 +216,26 @@ public class B3SingleFormatTest { } /** for example, parsing a w3c context */ - @Test public void parseB3SingleFormat_middleOfString_debugOnly() { + @Test void parseB3SingleFormat_middleOfString_debugOnly() { String input = "b2=foo,b3=d,b4=bar"; assertThat(parseB3SingleFormat(input, 10, 11).samplingFlags()) .isSameAs(SamplingFlags.DEBUG); } - @Test public void parseB3SingleFormat_middleOfString_incorrectIndex() { - String input = "b2=foo,b3=d,b4=bar"; - assertThat(parseB3SingleFormat(input, 10, 12)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_middleOfString_incorrectIndex() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform) - .log("Invalid input: only valid characters are lower-hex for {0}", "trace ID", null); + String input = "b2=foo,b3=d,b4=bar"; + assertThat(parseB3SingleFormat(input, 10, 12)) + .isNull(); // instead of raising exception + + verify(platform) + .log("Invalid input: only valid characters are lower-hex for {0}", "trace ID", null); + } } - @Test public void parseB3SingleFormat_spanIdsNotYetSampled() { + @Test void parseB3SingleFormat_spanIdsNotYetSampled() { assertThat(parseB3SingleFormat(traceId + "-" + spanId).context()) .isEqualToComparingFieldByField(TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) @@ -251,7 +243,7 @@ public class B3SingleFormatTest { ); } - @Test public void parseB3SingleFormat_spanIdsNotYetSampled128() { + @Test void parseB3SingleFormat_spanIdsNotYetSampled128() { assertThat(parseB3SingleFormat(traceIdHigh + traceId + "-" + spanId).context()) .isEqualToComparingFieldByField(TraceContext.newBuilder() .traceIdHigh(Long.parseUnsignedLong(traceIdHigh, 16)) @@ -260,7 +252,7 @@ public class B3SingleFormatTest { ); } - @Test public void parseB3SingleFormat_spanIdsUnsampled() { + @Test void parseB3SingleFormat_spanIdsUnsampled() { assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-0").context()) .isEqualToComparingFieldByField(TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) @@ -269,7 +261,7 @@ public class B3SingleFormatTest { ); } - @Test public void parseB3SingleFormat_parent_unsampled() { + @Test void parseB3SingleFormat_parent_unsampled() { assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-0-" + parentId).context()) .isEqualToComparingFieldByField(TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) @@ -279,7 +271,7 @@ public class B3SingleFormatTest { ); } - @Test public void parseB3SingleFormat_parent_debug() { + @Test void parseB3SingleFormat_parent_debug() { assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-d-" + parentId).context()) .isEqualToComparingFieldByField(TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) @@ -290,7 +282,7 @@ public class B3SingleFormatTest { } // odd but possible to not yet sample a child - @Test public void parseB3SingleFormat_parentid_notYetSampled() { + @Test void parseB3SingleFormat_parentid_notYetSampled() { assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-" + parentId).context()) .isEqualToComparingFieldByField(TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) @@ -299,7 +291,7 @@ public class B3SingleFormatTest { ); } - @Test public void parseB3SingleFormat_spanIdsWithDebug() { + @Test void parseB3SingleFormat_spanIdsWithDebug() { assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-d").context()) .isEqualToComparingFieldByField(TraceContext.newBuilder() .traceId(Long.parseUnsignedLong(traceId, 16)) @@ -308,214 +300,314 @@ public class B3SingleFormatTest { ); } - @Test public void parseB3SingleFormat_sampledFalse() { + @Test void parseB3SingleFormat_sampledFalse() { assertThat(parseB3SingleFormat("0")) .isEqualTo(TraceContextOrSamplingFlags.NOT_SAMPLED); } - @Test public void parseB3SingleFormat_sampled() { + @Test void parseB3SingleFormat_sampled() { assertThat(parseB3SingleFormat("1")) .isEqualTo(TraceContextOrSamplingFlags.SAMPLED); } - @Test public void parseB3SingleFormat_debug() { + @Test void parseB3SingleFormat_debug() { assertThat(parseB3SingleFormat("d")) .isEqualTo(TraceContextOrSamplingFlags.DEBUG); } /** This tests that the being index is inclusive and the end index is exclusive */ - @Test public void parseB3SingleFormat_ignoresBeforeAndAfter() { + @Test void parseB3SingleFormat_ignoresBeforeAndAfter() { String encoded = traceId + "-" + spanId; String sequence = "??" + encoded + "??"; assertThat(parseB3SingleFormat(sequence, 2, 2 + encoded.length())) .isEqualToComparingFieldByField(parseB3SingleFormat(encoded)); } - @Test public void parseB3SingleFormat_malformed() { - assertThat(parseB3SingleFormat("not-a-tumor")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_malformed() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat("not-a-tumor")) + .isNull(); // instead of raising exception - verify(platform) - .log("Invalid input: only valid characters are lower-hex for {0}", "trace ID", null); + verify(platform) + .log("Invalid input: only valid characters are lower-hex for {0}", "trace ID", null); + } } - @Test public void parseB3SingleFormat_malformed_notAscii() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId.substring(0, 15) + "💩")) - .isNull(); // instead of crashing + @Test void parseB3SingleFormat_malformed_notAscii() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform) - .log("Invalid input: only valid characters are lower-hex for {0}", "span ID", null); + assertThat(parseB3SingleFormat(traceId + "-" + spanId.substring(0, 15) + "💩")) + .isNull(); // instead of crashing + + verify(platform) + .log("Invalid input: only valid characters are lower-hex for {0}", "span ID", null); + } } - @Test public void parseB3SingleFormat_malformed_uuid() { - assertThat(parseB3SingleFormat("b970dafd-0d95-40aa-95d8-1d8725aebe40")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_malformed_uuid() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat("b970dafd-0d95-40aa-95d8-1d8725aebe40")) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: {0} is too short", "trace ID", null); + verify(platform).log("Invalid input: {0} is too short", "trace ID", null); + } } - @Test public void parseB3SingleFormat_malformed_hyphenForSampled() { - assertThat(parseB3SingleFormat("-")).isNull(); + @Test void parseB3SingleFormat_malformed_hyphenForSampled() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: expected 0, 1 or d for {0}", "sampled", null); + assertThat(parseB3SingleFormat("-")).isNull(); + + verify(platform).log("Invalid input: expected 0, 1 or d for {0}", "sampled", null); + } } - @Test public void parseB3SingleFormat_zero_traceId() { - assertThat( - parseB3SingleFormat("0000000000000000-" + spanId + "-1-" + parentId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_zero_traceId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: read all zeros {0}", "trace ID", null); + assertThat( + parseB3SingleFormat("0000000000000000-" + spanId + "-1-" + parentId)) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: read all zeros {0}", "trace ID", null); + } } - @Test public void parseB3SingleFormat_zero_spanId() { - assertThat( - parseB3SingleFormat(traceId + "-0000000000000000-1-" + parentId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_zero_spanId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: read all zeros {0}", "span ID", null); + assertThat( + parseB3SingleFormat(traceId + "-0000000000000000-1-" + parentId)) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: read all zeros {0}", "span ID", null); + } } /** Serializing parent ID as zero is the same as none. */ - @Test public void parseB3SingleFormat_zero_parentId() { + @Test void parseB3SingleFormat_zero_parentId() { assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-1-0000000000000000").context()) .isEqualToComparingFieldByField( parseB3SingleFormat(traceId + "-" + spanId + "-1").context() ); } - @Test public void parseB3SingleFormat_too_many_fields() { - assertThat( - parseB3SingleFormat(traceId + "-" + spanId + "-1-" + parentId + "-")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_too_many_fields() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: more than 4 fields exist", null); + assertThat( + parseB3SingleFormat(traceId + "-" + spanId + "-1-" + parentId + "-")) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: more than 4 fields exist", null); + } } - @Test public void parseB3SingleFormat_sampledCorrupt() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-f")) - .isNull(); // instead of crashing + @Test void parseB3SingleFormat_sampledCorrupt() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-f")) + .isNull(); // instead of crashing - verify(platform).log("Invalid input: expected 0, 1 or d for {0}", "sampled", null); + verify(platform).log("Invalid input: expected 0, 1 or d for {0}", "sampled", null); + } } - @Test public void parseB3SingleFormat_empty() { - assertThat(parseB3SingleFormat("")).isNull(); + @Test void parseB3SingleFormat_empty() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: empty", null); + assertThat(parseB3SingleFormat("")).isNull(); + + verify(platform).log("Invalid input: empty", null); + } } - @Test public void parseB3SingleFormat_empty_traceId() { - assertThat(parseB3SingleFormat("-234567812345678-" + spanId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_empty_traceId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat("-234567812345678-" + spanId)) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: empty {0}", "trace ID", null); + verify(platform).log("Invalid input: empty {0}", "trace ID", null); + } } - @Test public void parseB3SingleFormat_empty_spanId() { - assertThat(parseB3SingleFormat(traceId + "--")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_empty_spanId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceId + "--")) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: empty {0}", "span ID", null); + verify(platform).log("Invalid input: empty {0}", "span ID", null); + } } - @Test public void parseB3SingleFormat_empty_spanId_with_parent() { - assertThat(parseB3SingleFormat(traceId + "--" + parentId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_empty_spanId_with_parent() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: empty {0}", "span ID", null); + assertThat(parseB3SingleFormat(traceId + "--" + parentId)) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: empty {0}", "span ID", null); + } } /** We don't know if the intent was a sampled flag or a parent ID, but less logic to pick one. */ - @Test public void parseB3SingleFormat_empty_after_spanId() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_empty_after_spanId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-")) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: empty {0}", "sampled", null); + verify(platform).log("Invalid input: empty {0}", "sampled", null); + } } - @Test public void parseB3SingleFormat_empty_sampled_with_parentId() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "--" + parentId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_empty_sampled_with_parentId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: empty {0}", "sampled", null); + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "--" + parentId)) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: empty {0}", "sampled", null); + } } - @Test public void parseB3SingleFormat_empty_parent_after_sampled() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-d-")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_empty_parent_after_sampled() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-d-")) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: empty {0}", "parent ID", null); + verify(platform).log("Invalid input: empty {0}", "parent ID", null); + } } - @Test public void parseB3SingleFormat_truncated_traceId() { - assertThat(parseB3SingleFormat("1-" + spanId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_truncated_traceId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: {0} is too short", "trace ID", null); + assertThat(parseB3SingleFormat("1-" + spanId)) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: {0} is too short", "trace ID", null); + } } - @Test public void parseB3SingleFormat_truncated_traceId128() { - assertThat(parseB3SingleFormat(traceIdHigh.substring(0, 15) + traceId + "-" + spanId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_truncated_traceId128() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceIdHigh.substring(0, 15) + traceId + "-" + spanId)) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: {0} is too short", "trace ID", null); + verify(platform).log("Invalid input: {0} is too short", "trace ID", null); + } } - @Test public void parseB3SingleFormat_truncated_spanId() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId.substring(0, 15))) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_truncated_spanId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: {0} is too short", "span ID", null); + assertThat(parseB3SingleFormat(traceId + "-" + spanId.substring(0, 15))) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: {0} is too short", "span ID", null); + } } - @Test public void parseB3SingleFormat_truncated_parentId() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-" + parentId.substring(0, 15))) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_truncated_parentId() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-" + parentId.substring(0, 15))) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: {0} is too short", "parent ID", null); + verify(platform).log("Invalid input: {0} is too short", "parent ID", null); + } } - @Test public void parseB3SingleFormat_truncated_parentId_after_sampled() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-1-" + parentId.substring(0, 15))) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_truncated_parentId_after_sampled() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: {0} is too short", "parent ID", null); + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-1-" + parentId.substring(0, 15))) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: {0} is too short", "parent ID", null); + } } - @Test public void parseB3SingleFormat_traceIdTooLong() { - assertThat(parseB3SingleFormat(traceId + traceId + "a" + "-" + spanId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_traceIdTooLong() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceId + traceId + "a" + "-" + spanId)) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: {0} is too long", "trace ID", null); + verify(platform).log("Invalid input: {0} is too long", "trace ID", null); + } } - @Test public void parseB3SingleFormat_spanIdTooLong() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "a")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_spanIdTooLong() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: {0} is too long", "span ID", null); + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "a")) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: {0} is too long", "span ID", null); + } } /** Sampled too long without parent looks the same as a truncated parent ID */ - @Test public void parseB3SingleFormat_sampledTooLong() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-11-" + parentId)) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_sampledTooLong() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-11-" + parentId)) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: {0} is too long", "sampled", null); + verify(platform).log("Invalid input: {0} is too long", "sampled", null); + } } - @Test public void parseB3SingleFormat_parentIdTooLong() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-" + parentId + "a")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_parentIdTooLong() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - verify(platform).log("Invalid input: {0} is too long", "parent ID", null); + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-" + parentId + "a")) + .isNull(); // instead of raising exception + + verify(platform).log("Invalid input: {0} is too long", "parent ID", null); + } } - @Test public void parseB3SingleFormat_parentIdTooLong_afterSampled() { - assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-1-" + parentId + "a")) - .isNull(); // instead of raising exception + @Test void parseB3SingleFormat_parentIdTooLong_afterSampled() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + assertThat(parseB3SingleFormat(traceId + "-" + spanId + "-1-" + parentId + "a")) + .isNull(); // instead of raising exception - verify(platform).log("Invalid input: {0} is too long", "parent ID", null); + verify(platform).log("Invalid input: {0} is too long", "parent ID", null); + } } } diff --git a/brave/src/test/java/brave/sampler/RateLimitingSamplerTest.java b/brave/src/test/java/brave/sampler/RateLimitingSamplerTest.java index 2321702f99..4825416817 100644 --- a/brave/src/test/java/brave/sampler/RateLimitingSamplerTest.java +++ b/brave/src/test/java/brave/sampler/RateLimitingSamplerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -13,165 +13,182 @@ */ package brave.sampler; +import brave.internal.Platform; import java.util.concurrent.TimeUnit; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; import static brave.sampler.RateLimitingSampler.NANOS_PER_DECISECOND; import static brave.sampler.RateLimitingSampler.NANOS_PER_SECOND; import static org.assertj.core.api.Assertions.assertThat; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; - -@RunWith(PowerMockRunner.class) -// Added to declutter console: tells power mock not to mess with implicit classes we aren't testing -@PowerMockIgnore({"org.apache.logging.*", "javax.script.*"}) -@PrepareForTest(RateLimitingSampler.class) -public class RateLimitingSamplerTest { - - @Test public void samplesOnlySpecifiedNumber() { - mockStatic(System.class); - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND); - Sampler sampler = RateLimitingSampler.create(2); - - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + 1); - assertThat(sampler.isSampled(0L)).isTrue(); - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + 2); - assertThat(sampler.isSampled(0L)).isTrue(); - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + 2); - assertThat(sampler.isSampled(0L)).isFalse(); +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class RateLimitingSamplerTest { + @Mock Platform platform; + + @Test void samplesOnlySpecifiedNumber() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND); + Sampler sampler = RateLimitingSampler.create(2); + + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + 1); + assertThat(sampler.isSampled(0L)).isTrue(); + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + 2); + assertThat(sampler.isSampled(0L)).isTrue(); + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + 2); + assertThat(sampler.isSampled(0L)).isFalse(); + } } - @Test public void edgeCases() { - mockStatic(System.class); - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND); - Sampler sampler = RateLimitingSampler.create(2); + @Test void edgeCases() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND); + Sampler sampler = RateLimitingSampler.create(2); - // exact moment of reset - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND); - assertThat(sampler.isSampled(0L)).isTrue(); + // exact moment of reset + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND); + assertThat(sampler.isSampled(0L)).isTrue(); - // right before next interval - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_SECOND - 1); - assertThat(sampler.isSampled(0L)).isTrue(); - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_SECOND - 1); - assertThat(sampler.isSampled(0L)).isFalse(); + // right before next interval + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_SECOND - 1); + assertThat(sampler.isSampled(0L)).isTrue(); + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_SECOND - 1); + assertThat(sampler.isSampled(0L)).isFalse(); + } } - @Test public void resetsAfterASecond() { - mockStatic(System.class); - - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND); - Sampler sampler = RateLimitingSampler.create(10); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isFalse(); - - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_DECISECOND); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isFalse(); - - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_DECISECOND * 9); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isFalse(); - - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_SECOND); - assertThat(sampler.isSampled(0L)).isTrue(); + @Test void resetsAfterASecond() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND); + Sampler sampler = RateLimitingSampler.create(10); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isFalse(); + + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_DECISECOND); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isFalse(); + + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_DECISECOND * 9); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isFalse(); + + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_SECOND); + assertThat(sampler.isSampled(0L)).isTrue(); + } } - @Test public void resetsAfterALongGap() { - mockStatic(System.class); + @Test void resetsAfterALongGap() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - when(System.nanoTime()).thenReturn(0L); - Sampler sampler = RateLimitingSampler.create(10); + when(platform.nanoTime()).thenReturn(0L); + Sampler sampler = RateLimitingSampler.create(10); - // Try a really long time later. Makes sure extra credit isn't given, and no recursion errors - when(System.nanoTime()).thenReturn(TimeUnit.DAYS.toNanos(365)); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isFalse(); // we took the credit of the 1st decisecond + // Try a really long time later. Makes sure extra credit isn't given, and no recursion errors + when(platform.nanoTime()).thenReturn(TimeUnit.DAYS.toNanos(365)); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isFalse(); // we took the credit of the 1st decisecond + } } - @Test public void worksWithEdgeCases() { - mockStatic(System.class); - - when(System.nanoTime()).thenReturn(0L); - Sampler sampler = RateLimitingSampler.create(10); - - // try exact same nanosecond, however unlikely - assertThat(sampler.isSampled(0L)).isTrue(); // 1 - - // Try a value smaller than a decisecond, to ensure edge cases are covered - when(System.nanoTime()).thenReturn(1L); - assertThat(sampler.isSampled(0L)).isFalse(); // credit used - - // Try exactly a decisecond later, which should be a reset condition - when(System.nanoTime()).thenReturn(NANOS_PER_DECISECOND); - assertThat(sampler.isSampled(0L)).isTrue(); // 2 - assertThat(sampler.isSampled(0L)).isFalse(); // credit used - - // Try almost a second later - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND - 1); - assertThat(sampler.isSampled(0L)).isTrue(); // 3 - assertThat(sampler.isSampled(0L)).isTrue(); // 4 - assertThat(sampler.isSampled(0L)).isTrue(); // 5 - assertThat(sampler.isSampled(0L)).isTrue(); // 6 - assertThat(sampler.isSampled(0L)).isTrue(); // 7 - assertThat(sampler.isSampled(0L)).isTrue(); // 8 - assertThat(sampler.isSampled(0L)).isTrue(); // 9 - assertThat(sampler.isSampled(0L)).isTrue(); // 10 - assertThat(sampler.isSampled(0L)).isFalse(); // credit used - - // Try exactly a second later, which should be a reset condition - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND); - assertThat(sampler.isSampled(0L)).isTrue(); - assertThat(sampler.isSampled(0L)).isFalse(); // credit used + @Test void worksWithEdgeCases() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + when(platform.nanoTime()).thenReturn(0L); + Sampler sampler = RateLimitingSampler.create(10); + + // try exact same nanosecond, however unlikely + assertThat(sampler.isSampled(0L)).isTrue(); // 1 + + // Try a value smaller than a decisecond, to ensure edge cases are covered + when(platform.nanoTime()).thenReturn(1L); + assertThat(sampler.isSampled(0L)).isFalse(); // credit used + + // Try exactly a decisecond later, which should be a reset condition + when(platform.nanoTime()).thenReturn(NANOS_PER_DECISECOND); + assertThat(sampler.isSampled(0L)).isTrue(); // 2 + assertThat(sampler.isSampled(0L)).isFalse(); // credit used + + // Try almost a second later + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND - 1); + assertThat(sampler.isSampled(0L)).isTrue(); // 3 + assertThat(sampler.isSampled(0L)).isTrue(); // 4 + assertThat(sampler.isSampled(0L)).isTrue(); // 5 + assertThat(sampler.isSampled(0L)).isTrue(); // 6 + assertThat(sampler.isSampled(0L)).isTrue(); // 7 + assertThat(sampler.isSampled(0L)).isTrue(); // 8 + assertThat(sampler.isSampled(0L)).isTrue(); // 9 + assertThat(sampler.isSampled(0L)).isTrue(); // 10 + assertThat(sampler.isSampled(0L)).isFalse(); // credit used + + // Try exactly a second later, which should be a reset condition + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND); + assertThat(sampler.isSampled(0L)).isTrue(); + assertThat(sampler.isSampled(0L)).isFalse(); // credit used + } } - @Test public void allowsOddRates() { - mockStatic(System.class); - - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND); - Sampler sampler = RateLimitingSampler.create(11); - when(System.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_DECISECOND * 9); - for (int i = 0; i < 11; i++) { - assertThat(sampler.isSampled(0L)) - .withFailMessage("failed after " + (i + 1)) - .isTrue(); + @Test void allowsOddRates() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); + + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND); + Sampler sampler = RateLimitingSampler.create(11); + when(platform.nanoTime()).thenReturn(NANOS_PER_SECOND + NANOS_PER_DECISECOND * 9); + for (int i = 0; i < 11; i++) { + assertThat(sampler.isSampled(0L)) + .withFailMessage("failed after " + (i + 1)) + .isTrue(); + } + assertThat(sampler.isSampled(0L)).isFalse(); } - assertThat(sampler.isSampled(0L)).isFalse(); } - @Test public void worksOnRollover() { - mockStatic(System.class); - when(System.nanoTime()).thenReturn(-NANOS_PER_SECOND); - Sampler sampler = RateLimitingSampler.create(2); - assertThat(sampler.isSampled(0L)).isTrue(); + @Test void worksOnRollover() { + try (MockedStatic mb = mockStatic(Platform.class)) { + mb.when(Platform::get).thenReturn(platform); - when(System.nanoTime()).thenReturn(-NANOS_PER_SECOND / 2); - assertThat(sampler.isSampled(0L)).isTrue(); // second request + when(platform.nanoTime()).thenReturn(-NANOS_PER_SECOND); + Sampler sampler = RateLimitingSampler.create(2); + assertThat(sampler.isSampled(0L)).isTrue(); - when(System.nanoTime()).thenReturn(-NANOS_PER_SECOND / 4); - assertThat(sampler.isSampled(0L)).isFalse(); + when(platform.nanoTime()).thenReturn(-NANOS_PER_SECOND / 2); + assertThat(sampler.isSampled(0L)).isTrue(); // second request - when(System.nanoTime()).thenReturn(0L); // reset - assertThat(sampler.isSampled(0L)).isTrue(); + when(platform.nanoTime()).thenReturn(-NANOS_PER_SECOND / 4); + assertThat(sampler.isSampled(0L)).isFalse(); + + when(platform.nanoTime()).thenReturn(0L); // reset + assertThat(sampler.isSampled(0L)).isTrue(); + } } - @Test public void zeroMeansDropAllTraces() { + @Test void zeroMeansDropAllTraces() { assertThat(RateLimitingSampler.create(0)).isSameAs(Sampler.NEVER_SAMPLE); } - @Test(expected = IllegalArgumentException.class) - public void tracesPerSecond_cantBeNegative() { - RateLimitingSampler.create(-1); + @Test void tracesPerSecond_cantBeNegative() { + assertThatThrownBy(() -> RateLimitingSampler.create(-1)) + .isInstanceOf(IllegalArgumentException.class); } } diff --git a/build-bin/README.md b/build-bin/README.md index d2a245939a..363b6f7c0f 100755 --- a/build-bin/README.md +++ b/build-bin/README.md @@ -15,7 +15,7 @@ On deploy: `build-bin` holds portable scripts used in CI to test and deploy the project. The scripts here are portable. They do not include any CI provider-specific logic or ENV variables. -This helps `.travis.yml` and `test.yml` (GitHub Actions) contain nearly the same contents, even if +This helps `test.yml` (GitHub Actions) and alternatives contain nearly the same contents, even if certain OpenZipkin projects need slight adjustments here. Portability has proven necessary, as OpenZipkin has had to transition CI providers many times due to feature and quota constraints. @@ -41,8 +41,8 @@ blank. Tests should not run on documentation-only commits. Tests must not depend resources, as running tests can leak credentials. Git checkouts should include the full history so that license headers or other git analysis can take place. - * [configure_test] - Sets up build environment for tests. - * [test] - Builds and runs tests for this project. +* [configure_test] - Sets up build environment for tests. +* [test] - Builds and runs tests for this project. ### Example GitHub Actions setup @@ -53,9 +53,6 @@ the scripts it uses. The `on:` section obviates job creation and resource usage for irrelevant events. Notably, GitHub Actions includes the ability to skip documentation-only jobs. -Combine [configure_test] and [test] into the same `run:` when `configure_test` primes file system -cache. - Here's a partial `test.yml` including only the aspects mentioned above. ```yaml on: @@ -71,54 +68,13 @@ jobs: test: steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: - fetch-depth: 0 # full git history + fetch-depth: 0 # full git history for license check - name: Test - run: build-bin/configure_test && build-bin/test -``` - -### Example Travis setup -`.travis.yml` is a monolithic configuration file broken into stages, of which the default is "test". -A simplest Travis `test` job configures tests in `install` and runs them as `script`, but only on -relevant event conditions. - -The `if:` section obviates job creation and resource usage for irrelevant events. Travis does not -support file conditions. A `before_install` step to skip documentation-only commits will likely -complete in less than a minute (10 credit cost). - -Here's a partial `.travis.yml` including only the aspects mentioned above. -```yaml -git: - depth: false # TRAVIS_COMMIT_RANGE requires full commit history. - -jobs: - include: - - stage: test - if: branch = master AND tag IS blank AND type IN (push, pull_request) - name: Run unit and integration tests - before_install: | # Prevent test build of a documentation-only change. - if [ -n "${TRAVIS_COMMIT_RANGE}" ] && ! git diff --name-only "${TRAVIS_COMMIT_RANGE}" -- | grep -qv '\.md$'; then - echo "Stopping job as changes only affect documentation (ex. README.md)" - travis_terminate 0 - fi - install: ./build-bin/configure_test - script: ./build-bin/test -``` - -When Travis only runs tests (something else does deploy), there's no need to use stages: -```yaml -git: - depth: false # TRAVIS_COMMIT_RANGE requires full commit history. - -if: branch = master AND tag IS blank AND type IN (push, pull_request) -before_install: | # Prevent test build of a documentation-only change. - if [ -n "${TRAVIS_COMMIT_RANGE}" ] && ! git diff --name-only "${TRAVIS_COMMIT_RANGE}" -- | grep -qv '\.md$'; then - echo "Stopping job as changes only affect documentation (ex. README.md)" - travis_terminate 0 - fi -install: ./build-bin/configure_test -script: ./build-bin/test + run: | + build-bin/configure_test + build-bin/test ``` ## Deploy @@ -128,8 +84,8 @@ providers deploy pushes to master on when the tag is blank, but not on documenta Releases should deploy on version tags (ex `/^[0-9]+\.[0-9]+\.[0-9]+/`), without consideration of if the commit is documentation only or not. - * [configure_deploy] - Sets up environment and logs in, assuming [configure_test] was not called. - * [deploy] - deploys the project, with arg0 being "master" or a release commit like "1.2.3" +* [configure_deploy] - Sets up environment and logs in, assuming [configure_test] was not called. +* [deploy] - deploys the project, with arg0 being "master" or a release commit like "1.2.3" ### Example GitHub Actions setup @@ -141,77 +97,28 @@ The `on:` section obviates job creation and resource usage for irrelevant events cannot implement "master, except documentation only-commits" in the same file. Hence, deployments of master will happen even on README change. -Combine [configure_deploy] and [deploy] into the same `run:` when `configure_deploy` primes file -system cache. - Here's a partial `deploy.yml` including only the aspects mentioned above. Notice env variables are explicitly defined and `on.tags` is a [glob pattern](https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet). + ```yaml on: push: - tags: '[0-9]+.[0-9]+.[0-9]+**' # Ex. 8.272.10 or 15.0.1_p9 + tags: '[0-9]+.[0-9]+.[0-9]+**' # e.g. 8.272.10 or 15.0.1_p9 branches: master jobs: deploy: steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 1 # only needed to get the sha label - - name: Deploy + - name: Configure Deploy + run: build-bin/configure_deploy env: GH_USER: ${{ secrets.GH_USER }} GH_TOKEN: ${{ secrets.GH_TOKEN }} - run: | # GITHUB_REF will be refs/heads/master or refs/tags/MAJOR.MINOR.PATCH - build-bin/configure_deploy && - build-bin/deploy $(echo ${GITHUB_REF} | cut -d/ -f 3) -``` - -### Example Travis setup -`.travis.yml` is a monolithic configuration file broken into stages. This means `test` and `deploy` -are in the same file. A simplest Travis `deploy` stage has two jobs: one for master pushes and -another for version tags. These jobs are controlled by event conditions. - -The `if:` section obviates job creation and resource usage for irrelevant events. Travis does not -support file conditions. A `before_install` step to skip documentation-only commits will likely -complete in less than a minute (10 credit cost). - -As billing is by the minute, it is most cost effective to combine test and deploy on master push. - -Here's a partial `.travis.yml` including only the aspects mentioned above. Notice YAML anchors work -in Travis and `tag =~` [condition](https://github.com/travis-ci/travis-conditions) is a regular -expression. -```yaml -git: - depth: false # full git history for license check, and doc-only skipping - -_terminate_if_only_docs: &terminate_if_only_docs | - if [ -n "${TRAVIS_COMMIT_RANGE}" ] && ! git diff --name-only "${TRAVIS_COMMIT_RANGE}" -- | grep -qv '\.md$'; then - echo "Stopping job as changes only affect documentation (ex. README.md)" - travis_terminate 0 - fi - -jobs: - include: - - stage: test - if: branch = master AND tag IS blank AND type IN (push, pull_request) - before_install: *terminate_if_only_docs - install: | - if [ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] && [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then - export SHOULD_DEPLOY=true - ./build-bin/configure_deploy - else - export SHOULD_DEPLOY=false - ./build-bin/configure_test - fi - script: - - ./build-bin/test || travis_terminate 1 - - if [ "${SHOULD_DEPLOY}" != "true" ]; then travis_terminate 0; fi - - travis_wait ./build-bin/deploy master - - stage: deploy - # Ex. 8.272.10 or 15.0.1_p9 - if: tag =~ /^[0-9]+\.[0-9]+\.[0-9]+/ AND type = push AND env(GH_TOKEN) IS present - install: ./build-bin/configure_deploy - script: ./build-bin/deploy ${TRAVIS_TAG} + - name: Deploy + # GITHUB_REF will be refs/heads/master or refs/tags/1.2.3 + run: build-bin/deploy $(echo ${GITHUB_REF} | cut -d/ -f 3) ``` diff --git a/build-bin/docker/configure_docker b/build-bin/docker/configure_docker index 6168e889f3..2e09e33432 100755 --- a/build-bin/docker/configure_docker +++ b/build-bin/docker/configure_docker @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013-2020 The OpenZipkin Authors +# Copyright 2013-2023 The OpenZipkin Authors # # 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 @@ -25,13 +25,8 @@ set -ue # * checks.disable=true - saves time and a docker.io pull of alpine # * ryuk doesn't count against docker.io rate limits because Docker approved testcontainers as OSS echo checks.disable=true >> ~/.testcontainers.properties -# * upgrade ryuk until https://github.com/testcontainers/testcontainers-java/pull/3630 -echo ryuk.container.image=testcontainers/ryuk:0.3.1 >> ~/.testcontainers.properties # We don't use any docker.io images, but add a Google's mirror in case something implicitly does # * See https://cloud.google.com/container-registry/docs/pulling-cached-images echo '{ "registry-mirrors": ["https://mirror.gcr.io"] }' | sudo tee /etc/docker/daemon.json sudo service docker restart - -# * Ensure buildx and related features are disabled -mkdir -p ${HOME}/.docker && echo '{"experimental":"disabled"}' > ${HOME}/.docker/config.json diff --git a/build-bin/git/login_git b/build-bin/git/login_git index e168e7f98b..ebde366f8b 100755 --- a/build-bin/git/login_git +++ b/build-bin/git/login_git @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013-2020 The OpenZipkin Authors +# Copyright 2013-2023 The OpenZipkin Authors # # 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 diff --git a/build-bin/git/version_from_trigger_tag b/build-bin/git/version_from_trigger_tag index b6bdea2ab5..a1baf54fec 100755 --- a/build-bin/git/version_from_trigger_tag +++ b/build-bin/git/version_from_trigger_tag @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013-2020 The OpenZipkin Authors +# Copyright 2013-2023 The OpenZipkin Authors # # 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 @@ -15,9 +15,9 @@ set -ue -# This script echos a `N.M.L` version tag based on.. +# This script echos a `MAJOR.MINOR.PATCH` version tag based on.. # * arg1: XXXXX- prefix -# * arg2: XXXXX-N.M.L git trigger tag +# * arg2: XXXXX-MAJOR.MINOR.PATCH git trigger tag # # The script exits 1 if the prefix doesn't match. On success, the tag is deleted if it exists. # diff --git a/build-bin/gpg/configure_gpg b/build-bin/gpg/configure_gpg index 2ac150c2a5..ca347a8f76 100755 --- a/build-bin/gpg/configure_gpg +++ b/build-bin/gpg/configure_gpg @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013-2020 The OpenZipkin Authors +# Copyright 2013-2023 The OpenZipkin Authors # # 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 diff --git a/build-bin/maven/maven_deploy b/build-bin/maven/maven_deploy index c111e8157e..8fdd34fe2d 100755 --- a/build-bin/maven/maven_deploy +++ b/build-bin/maven/maven_deploy @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013-2020 The OpenZipkin Authors +# Copyright 2013-2023 The OpenZipkin Authors # # 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 diff --git a/build-bin/maven/maven_go_offline b/build-bin/maven/maven_go_offline index 57a41c48d5..ad78fac75f 100755 --- a/build-bin/maven/maven_go_offline +++ b/build-bin/maven/maven_go_offline @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013-2020 The OpenZipkin Authors +# Copyright 2013-2023 The OpenZipkin Authors # # 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 diff --git a/build-bin/maven/maven_opts b/build-bin/maven/maven_opts index 7d3eb67d7e..2432a1b9d1 100755 --- a/build-bin/maven/maven_opts +++ b/build-bin/maven/maven_opts @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013-2020 The OpenZipkin Authors +# Copyright 2013-2023 The OpenZipkin Authors # # 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 diff --git a/build-bin/maven/maven_release b/build-bin/maven/maven_release index 1a475325c0..d320da20c1 100755 --- a/build-bin/maven/maven_release +++ b/build-bin/maven/maven_release @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013-2020 The OpenZipkin Authors +# Copyright 2013-2023 The OpenZipkin Authors # # 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 diff --git a/build-bin/test b/build-bin/test index 134760b0e5..5802013958 100755 --- a/build-bin/test +++ b/build-bin/test @@ -4,5 +4,6 @@ # # See [README.md] for an explanation of this and how CI should use it. -# Don't use `-T1C ` because this project uses RetroLambda which is not threadsafe -./mvnw verify -nsu "$@" +# We use install, not verify, because maven-invoker-tests need brave-tests +# installed into the local repository before it can run. +./mvnw install -T1C -nsu "$@" diff --git a/context/jfr/pom.xml b/context/jfr/pom.xml index efc2638c8c..3ff46662bf 100644 --- a/context/jfr/pom.xml +++ b/context/jfr/pom.xml @@ -45,15 +45,6 @@ - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - org.codehaus.mojo animal-sniffer-maven-plugin diff --git a/context/log4j12/pom.xml b/context/log4j12/pom.xml index c6dfd8c003..7e64d2e13c 100644 --- a/context/log4j12/pom.xml +++ b/context/log4j12/pom.xml @@ -30,8 +30,6 @@ brave.context.log4j12 ${project.basedir}/../.. - 1.6 - java16 @@ -42,4 +40,40 @@ provided + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/context/log4j2/pom.xml b/context/log4j2/pom.xml index 5a79ab03c0..282a99cdb2 100644 --- a/context/log4j2/pom.xml +++ b/context/log4j2/pom.xml @@ -30,8 +30,6 @@ brave.context.log4j2 ${project.basedir}/../.. - 1.6 - java16 @@ -42,4 +40,40 @@ provided + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/context/rxjava2/pom.xml b/context/rxjava2/pom.xml index aa77e351f9..2583d6b73f 100644 --- a/context/rxjava2/pom.xml +++ b/context/rxjava2/pom.xml @@ -30,9 +30,6 @@ brave.context.rxjava2 ${project.basedir}/../.. - - 1.6 - java16 @@ -62,4 +59,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextCompletableObserver.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextCompletableObserver.java index 9be59d061a..ce8a550918 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextCompletableObserver.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextCompletableObserver.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -40,7 +40,7 @@ final class TraceContextCompletableObserver implements CompletableObserver, Disp @Override public void onError(Throwable t) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onError(t); } finally { scope.close(); @@ -49,7 +49,7 @@ final class TraceContextCompletableObserver implements CompletableObserver, Disp @Override public void onComplete() { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onComplete(); } finally { scope.close(); diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextConnectableFlowable.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextConnectableFlowable.java index cdba236ce4..e92268f3f2 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextConnectableFlowable.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextConnectableFlowable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -43,7 +43,7 @@ final class TraceContextConnectableFlowable extends ConnectableFlowable { @Override public void connect(Consumer connection) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { source.connect(connection); } finally { scope.close(); diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextConnectableObservable.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextConnectableObservable.java index 5a616a6dc5..eaddfd461a 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextConnectableObservable.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextConnectableObservable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -38,12 +38,12 @@ final class TraceContextConnectableObservable extends ConnectableObservable o) { - source.subscribe(new TraceContextObserver<>(o, contextScoper, assembled)); + source.subscribe(new TraceContextObserver(o, contextScoper, assembled)); } @Override public void connect(Consumer connection) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { source.connect(connection); } finally { scope.close(); diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextMaybe.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextMaybe.java index 0d29fe1b61..cf6580dc07 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextMaybe.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextMaybe.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -36,6 +36,6 @@ final class TraceContextMaybe extends Maybe { * subscription callbacks. */ @Override protected void subscribeActual(MaybeObserver o) { - source.subscribe(new TraceContextMaybeObserver<>(o, contextScoper, assembled)); + source.subscribe(new TraceContextMaybeObserver(o, contextScoper, assembled)); } } diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextMaybeObserver.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextMaybeObserver.java index e1d93de8b1..6cee2ebd23 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextMaybeObserver.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextMaybeObserver.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -43,7 +43,7 @@ final class TraceContextMaybeObserver implements MaybeObserver, Disposable @Override public void onError(Throwable t) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onError(t); } finally { scope.close(); @@ -52,7 +52,7 @@ final class TraceContextMaybeObserver implements MaybeObserver, Disposable @Override public void onSuccess(T value) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onSuccess(value); } finally { scope.close(); @@ -61,7 +61,7 @@ final class TraceContextMaybeObserver implements MaybeObserver, Disposable @Override public void onComplete() { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onComplete(); } finally { scope.close(); diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextObservable.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextObservable.java index d83f8a2976..fd1332754f 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextObservable.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextObservable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -36,6 +36,6 @@ final class TraceContextObservable extends Observable { * subscription callbacks. */ @Override protected void subscribeActual(Observer o) { - source.subscribe(new TraceContextObserver<>(o, contextScoper, assembled)); + source.subscribe(new TraceContextObserver(o, contextScoper, assembled)); } } diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextObserver.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextObserver.java index bdf13e6def..3c45feab08 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextObserver.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextObserver.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -45,7 +45,7 @@ final class TraceContextObserver implements Observer, Disposable { @Override public void onNext(T t) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onNext(t); } finally { scope.close(); @@ -60,7 +60,7 @@ final class TraceContextObserver implements Observer, Disposable { done = true; Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onError(t); } finally { scope.close(); @@ -72,7 +72,7 @@ final class TraceContextObserver implements Observer, Disposable { done = true; Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onComplete(); } finally { scope.close(); diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSingle.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSingle.java index 12cc69058d..c03e440773 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSingle.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSingle.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -36,6 +36,6 @@ final class TraceContextSingle extends Single { * subscription callbacks. */ @Override protected void subscribeActual(SingleObserver o) { - source.subscribe(new TraceContextSingleObserver<>(o, contextScoper, assembled)); + source.subscribe(new TraceContextSingleObserver(o, contextScoper, assembled)); } } diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSingleObserver.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSingleObserver.java index 616a536b39..d06eb9e4d8 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSingleObserver.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSingleObserver.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -43,7 +43,7 @@ final class TraceContextSingleObserver implements SingleObserver, Disposab @Override public void onError(Throwable t) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onError(t); } finally { scope.close(); @@ -52,7 +52,7 @@ final class TraceContextSingleObserver implements SingleObserver, Disposab @Override public void onSuccess(T value) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onSuccess(value); } finally { scope.close(); diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSubscriber.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSubscriber.java index 934e36f40d..721503f372 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSubscriber.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/TraceContextSubscriber.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -46,7 +46,7 @@ class TraceContextSubscriber implements Subscriber { @Override public void onNext(T t) { Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onNext(t); } finally { scope.close(); @@ -61,7 +61,7 @@ class TraceContextSubscriber implements Subscriber { done = true; Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onError(t); } finally { scope.close(); @@ -73,7 +73,7 @@ class TraceContextSubscriber implements Subscriber { done = true; Scope scope = contextScoper.maybeScope(assembled); - try { // retrolambda can't resolve this try/finally + try { downstream.onComplete(); } finally { scope.close(); diff --git a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/Wrappers.java b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/Wrappers.java index 696cbb9b22..0fa6f9c260 100644 --- a/context/rxjava2/src/main/java/brave/context/rxjava2/internal/Wrappers.java +++ b/context/rxjava2/src/main/java/brave/context/rxjava2/internal/Wrappers.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -41,16 +41,16 @@ public class Wrappers { public static Subscriber wrap( Subscriber downstream, CurrentTraceContext contextScoper, TraceContext assembled) { if (downstream instanceof FlowableSubscriber) { - return new TraceContextFlowableSubscriber<>((FlowableSubscriber) downstream, + return new TraceContextFlowableSubscriber((FlowableSubscriber) downstream, contextScoper, assembled); } - return new TraceContextSubscriber<>(downstream, contextScoper, assembled); + return new TraceContextSubscriber(downstream, contextScoper, assembled); } public static Completable wrap( CompletableSource source, CurrentTraceContext contextScoper, TraceContext assembled) { if (source instanceof Callable) { - return new TraceContextCallableCompletable<>(source, contextScoper, assembled); + return new TraceContextCallableCompletable(source, contextScoper, assembled); } return new TraceContextCompletable(source, contextScoper, assembled); } @@ -58,63 +58,63 @@ public static Completable wrap( public static Maybe wrap( MaybeSource source, CurrentTraceContext contextScoper, TraceContext assembled) { if (source instanceof Callable) { - return new TraceContextCallableMaybe<>(source, contextScoper, assembled); + return new TraceContextCallableMaybe(source, contextScoper, assembled); } - return new TraceContextMaybe<>(source, contextScoper, assembled); + return new TraceContextMaybe(source, contextScoper, assembled); } public static Single wrap( SingleSource source, CurrentTraceContext contextScoper, TraceContext assembled) { if (source instanceof Callable) { - return new TraceContextCallableSingle<>(source, contextScoper, assembled); + return new TraceContextCallableSingle(source, contextScoper, assembled); } - return new TraceContextSingle<>(source, contextScoper, assembled); + return new TraceContextSingle(source, contextScoper, assembled); } public static Observable wrap( ObservableSource source, CurrentTraceContext contextScoper, TraceContext assembled) { if (source instanceof Callable) { - return new TraceContextCallableObservable<>(source, contextScoper, assembled); + return new TraceContextCallableObservable(source, contextScoper, assembled); } - return new TraceContextObservable<>(source, contextScoper, assembled); + return new TraceContextObservable(source, contextScoper, assembled); } public static ConnectableObservable wrap( ConnectableObservable source, CurrentTraceContext contextScoper, TraceContext assembled) { - return new TraceContextConnectableObservable<>(source, contextScoper, assembled); + return new TraceContextConnectableObservable(source, contextScoper, assembled); } public static Flowable wrap( Publisher source, CurrentTraceContext contextScoper, TraceContext assembled) { if (source instanceof Callable) { - return new TraceContextCallableFlowable<>(source, contextScoper, assembled); + return new TraceContextCallableFlowable(source, contextScoper, assembled); } - return new TraceContextFlowable<>(source, contextScoper, assembled); + return new TraceContextFlowable(source, contextScoper, assembled); } public static ConnectableFlowable wrap( ConnectableFlowable source, CurrentTraceContext contextScoper, TraceContext assembled) { - return new TraceContextConnectableFlowable<>(source, contextScoper, assembled); + return new TraceContextConnectableFlowable(source, contextScoper, assembled); } public static ParallelFlowable wrap( ParallelFlowable source, CurrentTraceContext contextScoper, TraceContext assembled) { - return new TraceContextParallelFlowable<>(source, contextScoper, assembled); + return new TraceContextParallelFlowable(source, contextScoper, assembled); } public static Observer wrap(Observer downstream, CurrentTraceContext contextScoper, TraceContext assembled) { - return new TraceContextObserver<>(downstream, contextScoper, assembled); + return new TraceContextObserver(downstream, contextScoper, assembled); } public static SingleObserver wrap(SingleObserver downstream, CurrentTraceContext contextScoper, TraceContext assembled) { - return new TraceContextSingleObserver<>(downstream, contextScoper, assembled); + return new TraceContextSingleObserver(downstream, contextScoper, assembled); } public static MaybeObserver wrap(MaybeObserver downstream, CurrentTraceContext contextScoper, TraceContext assembled) { - return new TraceContextMaybeObserver<>(downstream, contextScoper, assembled); + return new TraceContextMaybeObserver(downstream, contextScoper, assembled); } public static CompletableObserver wrap(CompletableObserver downstream, diff --git a/context/slf4j/pom.xml b/context/slf4j/pom.xml index 4e27a3bf51..fa3a69aa58 100644 --- a/context/slf4j/pom.xml +++ b/context/slf4j/pom.xml @@ -30,8 +30,6 @@ brave.context.slf4j ${project.basedir}/../.. - 1.6 - java16 @@ -42,4 +40,40 @@ provided + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/benchmarks/pom.xml b/instrumentation/benchmarks/pom.xml index ab64c24865..47992ebead 100644 --- a/instrumentation/benchmarks/pom.xml +++ b/instrumentation/benchmarks/pom.xml @@ -250,15 +250,6 @@ - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - maven-shade-plugin diff --git a/instrumentation/dubbo-rpc/pom.xml b/instrumentation/dubbo-rpc/pom.xml index 5a12c8acfc..99035474d6 100644 --- a/instrumentation/dubbo-rpc/pom.xml +++ b/instrumentation/dubbo-rpc/pom.xml @@ -29,8 +29,6 @@ brave.dubbo.rpc ${project.basedir}/../.. - 1.6 - java16 2.6.9 @@ -62,4 +60,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingFilter.java b/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingFilter.java index c4870f274a..7213b9e0e1 100644 --- a/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingFilter.java +++ b/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -140,10 +140,13 @@ public void setRpcTracing(RpcTracing rpcTracing) { TraceContext callbackContext = kind == Kind.CLIENT ? invocationContext : span.context(); ResponseFuture wrapped = new FinishSpanResponseFuture(original, this, request, result, span, callbackContext); - RpcContext.getContext().setFuture(new FutureAdapter<>(wrapped)); + RpcContext.getContext().setFuture(new FutureAdapter(wrapped)); } return result; - } catch (Throwable e) { + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { propagateIfFatal(e); error = e; throw e; diff --git a/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingResponseCallback.java b/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingResponseCallback.java index bcd3b1e42f..4363b679c4 100644 --- a/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingResponseCallback.java +++ b/instrumentation/dubbo-rpc/src/main/java/brave/dubbo/rpc/TracingResponseCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -60,18 +60,22 @@ static final class DelegateAndFinishSpan extends TracingResponseCallback { } @Override public void done(Object response) { - try (Scope ws = current.maybeScope(context)) { + Scope ws = current.maybeScope(context); + try { delegate.done(response); } finally { super.done(response); + ws.close(); } } @Override public void caught(Throwable exception) { - try (Scope ws = current.maybeScope(context)) { + Scope ws = current.maybeScope(context); + try { delegate.caught(exception); } finally { super.caught(exception); + ws.close(); } } } diff --git a/instrumentation/grpc/pom.xml b/instrumentation/grpc/pom.xml index 598aba3122..0fdc796ff7 100644 --- a/instrumentation/grpc/pom.xml +++ b/instrumentation/grpc/pom.xml @@ -29,9 +29,6 @@ brave.grpc ${project.basedir}/../.. - - 1.6 - java16 1.6.2 0.6.1 @@ -114,7 +111,42 @@ + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + include-invoker @@ -146,6 +178,7 @@ grpc-all ${old-grpc.version} MAIN + jar org.codehaus.mojo diff --git a/instrumentation/grpc/src/it/grpc12/pom.xml b/instrumentation/grpc/src/it/grpc12/pom.xml index 9942dec9f9..d8b9d25df9 100644 --- a/instrumentation/grpc/src/it/grpc12/pom.xml +++ b/instrumentation/grpc/src/it/grpc12/pom.xml @@ -1,7 +1,7 @@ false + us to change scope and format via log4j2.properties --> org.apache.logging.log4j.jul.LogManager diff --git a/instrumentation/grpc/src/main/java/brave/grpc/GrpcPropagation.java b/instrumentation/grpc/src/main/java/brave/grpc/GrpcPropagation.java index 448a331be6..253c6e7a40 100644 --- a/instrumentation/grpc/src/main/java/brave/grpc/GrpcPropagation.java +++ b/instrumentation/grpc/src/main/java/brave/grpc/GrpcPropagation.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -48,7 +48,7 @@ final class GrpcPropagation implements Propagation { /** Creates constant keys for use in propagating trace identifiers or baggage. */ static Map> nameToKey(Propagation propagation) { - Map> result = new LinkedHashMap<>(); + Map> result = new LinkedHashMap>(); for (String keyName : propagation.keys()) { result.put(keyName, Key.of(keyName, Metadata.ASCII_STRING_MARSHALLER)); } @@ -83,11 +83,11 @@ static Propagation create(Propagation delegate) { } @Override public Injector injector(Setter setter) { - return new GrpcInjector<>(this, setter); + return new GrpcInjector(this, setter); } @Override public Extractor extractor(Getter getter) { - return new GrpcExtractor<>(this, getter); + return new GrpcExtractor(this, getter); } static final class GrpcInjector implements Injector { diff --git a/instrumentation/grpc/src/main/java/brave/grpc/TraceContextBinaryFormat.java b/instrumentation/grpc/src/main/java/brave/grpc/TraceContextBinaryFormat.java index 905d8c141a..5563c0fbc6 100644 --- a/instrumentation/grpc/src/main/java/brave/grpc/TraceContextBinaryFormat.java +++ b/instrumentation/grpc/src/main/java/brave/grpc/TraceContextBinaryFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -97,7 +97,7 @@ static byte[] toBytes(TraceContext traceContext) { .traceId(traceId) .spanId(spanId); if (sampled != null) builder.sampled(sampled.booleanValue()); - if (tags != null) builder.extra(Collections.singletonList(tags)); + if (tags != null) builder.extra(Collections.singletonList(tags)); return builder.build(); } diff --git a/instrumentation/grpc/src/main/java/brave/grpc/TracingClientInterceptor.java b/instrumentation/grpc/src/main/java/brave/grpc/TracingClientInterceptor.java index 81fbb897df..905f199bb6 100644 --- a/instrumentation/grpc/src/main/java/brave/grpc/TracingClientInterceptor.java +++ b/instrumentation/grpc/src/main/java/brave/grpc/TracingClientInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -42,7 +42,6 @@ final class TracingClientInterceptor implements ClientInterceptor { final Map> nameToKey; final CurrentTraceContext currentTraceContext; final RpcClientHandler handler; - final MessageProcessor messageProcessor; TracingClientInterceptor(GrpcTracing grpcTracing) { @@ -55,7 +54,7 @@ final class TracingClientInterceptor implements ClientInterceptor { @Override public ClientCall interceptCall(MethodDescriptor method, CallOptions callOptions, Channel next) { - return new TracingClientCall<>( + return new TracingClientCall( method, callOptions, currentTraceContext.get(), next.newCall(method, callOptions)); } @@ -63,7 +62,7 @@ final class TracingClientCall extends SimpleForwardingClientCall method; final CallOptions callOptions; final TraceContext invocationContext; - final AtomicReference spanRef = new AtomicReference<>(); + final AtomicReference spanRef = new AtomicReference(); TracingClientCall(MethodDescriptor method, CallOptions callOptions, TraceContext invocationContext, ClientCall call) { @@ -80,62 +79,89 @@ final class TracingClientCall extends SimpleForwardingClientCall( + responseListener = new TracingClientCallListener( responseListener, invocationContext, spanRef, request ); - try (Scope scope = currentTraceContext.maybeScope(span.context())) { + Scope scope = currentTraceContext.maybeScope(span.context()); + Throwable error = null; + try { super.start(responseListener, headers); - } catch (Throwable e) { + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { propagateIfFatal(e); - - // Another interceptor may throw an exception during start, in which case no other - // callbacks are called, so go ahead and close the span here. - // - // See instrumentation/grpc/RATIONALE.md for why we don't use the handler here - spanRef.set(null); - if (span != null) span.error(e).finish(); + error = e; throw e; + } finally { + if (error != null) { + // Another interceptor may throw an exception during start, in which case no other + // callbacks are called, so go ahead and close the span here. + // + // See instrumentation/grpc/RATIONALE.md for why we don't use the handler here + spanRef.set(null); + if (span != null) span.error(error).finish(); + } + scope.close(); } } @Override public void cancel(@Nullable String message, @Nullable Throwable cause) { - try (Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext)) { + Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext); + try { delegate().cancel(message, cause); + } finally { + scope.close(); } } @Override public void halfClose() { - try (Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext)) { + Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext); + Throwable error = null; + try { delegate().halfClose(); - } catch (Throwable e) { + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { propagateIfFatal(e); - - // If there was an exception executing onHalfClose, we don't expect other lifecycle - // commands to succeed. Accordingly, we close the span - // - // See instrumentation/grpc/RATIONALE.md for why we don't use the handler here - Span span = spanRef.getAndSet(null); - if (span != null) span.error(e).finish(); + error = e; throw e; + } finally { + if (error != null) { + // If there was an exception executing onHalfClose, we don't expect other lifecycle + // commands to succeed. Accordingly, we close the span + // + // See instrumentation/grpc/RATIONALE.md for why we don't use the handler here + Span span = spanRef.getAndSet(null); + if (span != null) span.error(error).finish(); + } + scope.close(); } } @Override public void request(int numMessages) { - try (Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext)) { + Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext); + try { delegate().request(numMessages); + } finally { + scope.close(); } } @Override public void sendMessage(ReqT message) { - try (Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext)) { + Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext); + try { delegate().sendMessage(message); Span span = spanRef.get(); // could be an error SpanCustomizer customizer = span != null ? span.customizer() : NoopSpanCustomizer.INSTANCE; messageProcessor.onMessageSent(message, customizer); + } finally { + scope.close(); } } } @@ -169,8 +195,11 @@ final class TracingClientCallListener extends SimpleForwardingClientCallL } @Override public void onReady() { - try (Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext)) { + Scope scope = maybeScopeClientOrInvocationContext(spanRef, invocationContext); + try { delegate().onReady(); + } finally { + scope.close(); } } @@ -178,17 +207,23 @@ final class TracingClientCallListener extends SimpleForwardingClientCallL @Override public void onHeaders(Metadata headers) { // onHeaders() JavaDoc mentions headers are not thread-safe, so we make a safe copy here. this.headers.merge(headers); - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate().onHeaders(headers); + } finally { + scope.close(); } } @Override public void onMessage(RespT message) { - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { Span span = spanRef.get(); // could be an error SpanCustomizer customizer = span != null ? span.customizer() : NoopSpanCustomizer.INSTANCE; messageProcessor.onMessageReceived(message, customizer); delegate().onMessage(message); + } finally { + scope.close(); } } @@ -198,8 +233,11 @@ final class TracingClientCallListener extends SimpleForwardingClientCallL Span span = spanRef.getAndSet(null); if (span != null) handler.handleReceive(response, span); - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate().onClose(status, trailers); + } finally { + scope.close(); } } } diff --git a/instrumentation/grpc/src/main/java/brave/grpc/TracingServerInterceptor.java b/instrumentation/grpc/src/main/java/brave/grpc/TracingServerInterceptor.java index 5f57b10167..98319a052f 100644 --- a/instrumentation/grpc/src/main/java/brave/grpc/TracingServerInterceptor.java +++ b/instrumentation/grpc/src/main/java/brave/grpc/TracingServerInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -32,6 +32,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import static brave.internal.Throwables.propagateIfFatal; + // not exposed directly as implementation notably changes between versions 1.2 and 1.3 final class TracingServerInterceptor implements ServerInterceptor { final Map> nameToKey; @@ -54,23 +56,35 @@ public Listener interceptCall(ServerCall call, GrpcServerRequest request = new GrpcServerRequest(nameToKey, call, headers); Span span = handler.handleReceive(request); - AtomicReference spanRef = new AtomicReference<>(span); + AtomicReference spanRef = new AtomicReference(span); // startCall invokes user interceptors, so we place the span in scope here Listener result; - try (Scope scope = currentTraceContext.maybeScope(span.context())) { - result = next.startCall(new TracingServerCall<>(call, span, spanRef, request), headers); - } catch (Throwable e) { - // Another interceptor may throw an exception during startCall, in which case no other - // callbacks are called, so go ahead and close the span here. - // - // See instrumentation/grpc/RATIONALE.md for why we don't use the handler here - spanRef.set(null); - if (span != null) span.error(e).finish(); + Throwable error = null; + Scope scope = currentTraceContext.maybeScope(span.context()); + try { + result = + next.startCall(new TracingServerCall(call, span, spanRef, request), headers); + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; throw e; + } finally { + if (error != null) { + // Another interceptor may throw an exception during startCall, in which case no other + // callbacks are called, so go ahead and close the span here. + // + // See instrumentation/grpc/RATIONALE.md for why we don't use the handler here + spanRef.set(null); + if (span != null) span.error(error).finish(); + } + scope.close(); } - return new TracingServerCallListener<>(result, span, spanRef, request); + return new TracingServerCallListener(result, span, spanRef, request); } final class TracingServerCall extends SimpleForwardingServerCall { @@ -88,25 +102,34 @@ final class TracingServerCall extends SimpleForwardingServerCall extends SimpleForwardingServerCall extends SimpleForwardingServerCallL } @Override public void onMessage(RespT message) { - try (Scope scope = currentTraceContext.maybeScope(context)) { + Scope scope = currentTraceContext.maybeScope(context); + try { delegate().onMessage(message); Span span = spanRef.get(); // could be an error SpanCustomizer customizer = span != null ? span.customizer() : NoopSpanCustomizer.INSTANCE; messageProcessor.onMessageReceived(message, customizer); + } finally { + scope.close(); } } @Override public void onHalfClose() { - try (Scope scope = currentTraceContext.maybeScope(context)) { + Scope scope = currentTraceContext.maybeScope(context); + Throwable error = null; + try { delegate().onHalfClose(); - } catch (Throwable e) { - // If there was an exception executing onHalfClose, we don't expect other lifecycle - // commands to succeed. Accordingly, we close the span - // - // See instrumentation/grpc/RATIONALE.md for why we don't use the handler here - Span span = spanRef.getAndSet(null); - if (span != null) span.error(e).finish(); - + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; throw e; + } finally { + if (error != null) { + // If there was an exception executing onHalfClose, we don't expect other lifecycle + // commands to succeed. Accordingly, we close the span + // + // See instrumentation/grpc/RATIONALE.md for why we don't use the handler here + Span span = spanRef.getAndSet(null); + if (span != null) span.error(error).finish(); + } + scope.close(); } } @Override public void onCancel() { - try (Scope scope = currentTraceContext.maybeScope(context)) { + Scope scope = currentTraceContext.maybeScope(context); + try { delegate().onCancel(); + } finally { + scope.close(); } } @Override public void onComplete() { - try (Scope scope = currentTraceContext.maybeScope(context)) { + Scope scope = currentTraceContext.maybeScope(context); + try { delegate().onComplete(); + } finally { + scope.close(); } } @Override public void onReady() { - try (Scope scope = currentTraceContext.maybeScope(context)) { + Scope scope = currentTraceContext.maybeScope(context); + try { delegate().onReady(); + } finally { + scope.close(); } } } diff --git a/instrumentation/http-tests-jakarta/pom.xml b/instrumentation/http-tests-jakarta/pom.xml index 3c7889b23a..adfa8bbc62 100644 --- a/instrumentation/http-tests-jakarta/pom.xml +++ b/instrumentation/http-tests-jakarta/pom.xml @@ -87,15 +87,6 @@ - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - org.codehaus.mojo animal-sniffer-maven-plugin diff --git a/instrumentation/http-tests/pom.xml b/instrumentation/http-tests/pom.xml index 6053d2f912..5aa5e4890e 100644 --- a/instrumentation/http-tests/pom.xml +++ b/instrumentation/http-tests/pom.xml @@ -85,15 +85,6 @@ - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - diff --git a/instrumentation/http/pom.xml b/instrumentation/http/pom.xml index 91dbd7cb00..492a1fca59 100644 --- a/instrumentation/http/pom.xml +++ b/instrumentation/http/pom.xml @@ -30,8 +30,6 @@ brave.http ${project.basedir}/../.. - 1.6 - java16 @@ -50,4 +48,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/http/src/main/java/brave/http/HttpClientHandler.java b/instrumentation/http/src/main/java/brave/http/HttpClientHandler.java index d8017ed710..aca15b88ca 100644 --- a/instrumentation/http/src/main/java/brave/http/HttpClientHandler.java +++ b/instrumentation/http/src/main/java/brave/http/HttpClientHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -69,7 +69,7 @@ public final class HttpClientHandler extends HttpHandler { public static HttpClientHandler create( HttpTracing httpTracing) { if (httpTracing == null) throw new NullPointerException("httpTracing == null"); - return new HttpClientHandler<>(httpTracing, null); + return new HttpClientHandler(httpTracing, null); } /** @@ -81,7 +81,7 @@ public static HttpClientHandler create(HttpTracing httpTr HttpClientAdapter adapter) { if (httpTracing == null) throw new NullPointerException("httpTracing == null"); if (adapter == null) throw new NullPointerException("adapter == null"); - return new HttpClientHandler<>(httpTracing, adapter); + return new HttpClientHandler(httpTracing, adapter); } final Tracer tracer; @@ -190,7 +190,7 @@ public Span handleSend(HttpClientRequest request, Span span) { if (request instanceof HttpClientRequest) { clientRequest = (HttpClientRequest) request; } else { - clientRequest = new HttpClientAdapters.FromRequestAdapter<>(adapter, request); + clientRequest = new HttpClientAdapters.FromRequestAdapter(adapter, request); } return handleStart(clientRequest, span); @@ -207,7 +207,7 @@ public Span handleSend(HttpClientRequest request, Span span) { if (request instanceof HttpClientRequest) { clientRequest = (HttpClientRequest) request; } else { - clientRequest = new FromRequestAdapter<>(adapter, request); + clientRequest = new FromRequestAdapter(adapter, request); } return tracer.nextSpan(httpSampler, clientRequest); } @@ -236,7 +236,7 @@ public void handleReceive(@Nullable Resp response, @Nullable Throwable error, Sp span.error(error); } } else { - clientResponse = new FromResponseAdapter<>(adapter, response, error); + clientResponse = new FromResponseAdapter(adapter, response, error); } handleFinish(clientResponse, span); } diff --git a/instrumentation/http/src/main/java/brave/http/HttpClientParserAdapter.java b/instrumentation/http/src/main/java/brave/http/HttpClientParserAdapter.java index 2936dc1bb2..20e2c7c297 100644 --- a/instrumentation/http/src/main/java/brave/http/HttpClientParserAdapter.java +++ b/instrumentation/http/src/main/java/brave/http/HttpClientParserAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -44,7 +44,7 @@ public void request(HttpAdapter adapter, Req req, SpanCustomizer customizer) { HttpRequest request; if (req instanceof HttpClientRequest) { - request = new HttpClientAdapters.FromRequestAdapter<>((HttpClientAdapter) adapter, req); + request = new HttpClientAdapters.FromRequestAdapter((HttpClientAdapter) adapter, req); } else if (adapter instanceof HttpClientAdapters.ToRequestAdapter) { request = ((HttpClientAdapters.ToRequestAdapter) adapter).delegate; } else { @@ -58,7 +58,7 @@ public void request(HttpAdapter adapter, Req req, SpanCustomizer c HttpResponse response; if (res instanceof HttpClientResponse) { response = - new HttpClientAdapters.FromResponseAdapter<>((HttpClientAdapter) adapter, res, error); + new HttpClientAdapters.FromResponseAdapter((HttpClientAdapter) adapter, res, error); } else if (adapter instanceof HttpClientAdapters.ToResponseAdapter) { response = ((HttpClientAdapters.ToResponseAdapter) adapter).delegate; } else { diff --git a/instrumentation/http/src/main/java/brave/http/HttpRuleSampler.java b/instrumentation/http/src/main/java/brave/http/HttpRuleSampler.java index 2efb4e149e..3d54892774 100644 --- a/instrumentation/http/src/main/java/brave/http/HttpRuleSampler.java +++ b/instrumentation/http/src/main/java/brave/http/HttpRuleSampler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -136,6 +136,6 @@ public HttpRuleSampler build() { @Override @Deprecated public Boolean trySample(HttpAdapter adapter, Req request) { if (request == null) return null; - return trySample(new FromHttpAdapter<>(adapter, request)); + return trySample(new FromHttpAdapter(adapter, request)); } } diff --git a/instrumentation/http/src/main/java/brave/http/HttpSampler.java b/instrumentation/http/src/main/java/brave/http/HttpSampler.java index 01f11fde26..ed04d36ee0 100644 --- a/instrumentation/http/src/main/java/brave/http/HttpSampler.java +++ b/instrumentation/http/src/main/java/brave/http/HttpSampler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -129,7 +129,7 @@ static final class HttpRequestSamplerAdapter extends HttpSampler { if (request instanceof HttpRequest) { return delegate.trySample((HttpRequest) request); } - return delegate.trySample(new FromHttpAdapter<>(adapter, request)); + return delegate.trySample(new FromHttpAdapter(adapter, request)); } @Override public String toString() { diff --git a/instrumentation/http/src/main/java/brave/http/HttpServerHandler.java b/instrumentation/http/src/main/java/brave/http/HttpServerHandler.java index 9ec2adc32f..99ba5c2aac 100644 --- a/instrumentation/http/src/main/java/brave/http/HttpServerHandler.java +++ b/instrumentation/http/src/main/java/brave/http/HttpServerHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -67,7 +67,7 @@ public final class HttpServerHandler extends HttpHandler { public static HttpServerHandler create( HttpTracing httpTracing) { if (httpTracing == null) throw new NullPointerException("httpTracing == null"); - return new HttpServerHandler<>(httpTracing, null); + return new HttpServerHandler(httpTracing, null); } /** @@ -79,7 +79,7 @@ public static HttpServerHandler create(HttpTracing httpTr HttpServerAdapter adapter) { if (httpTracing == null) throw new NullPointerException("httpTracing == null"); if (adapter == null) throw new NullPointerException("adapter == null"); - return new HttpServerHandler<>(httpTracing, adapter); + return new HttpServerHandler(httpTracing, adapter); } final Tracer tracer; @@ -131,7 +131,7 @@ public Span handleReceive(HttpServerRequest request) { if (request instanceof HttpServerRequest) { serverRequest = (HttpServerRequest) request; } else { - serverRequest = new HttpServerAdapters.FromRequestAdapter<>(adapter, request); + serverRequest = new HttpServerAdapters.FromRequestAdapter(adapter, request); } Span span = nextSpan(extractor.extract(carrier), serverRequest); @@ -178,7 +178,7 @@ public void handleSend(@Nullable Resp response, @Nullable Throwable error, Span span.error(error); } } else { - serverResponse = new FromResponseAdapter<>(adapter, response, error); + serverResponse = new FromResponseAdapter(adapter, response, error); } handleFinish(serverResponse, span); } diff --git a/instrumentation/http/src/main/java/brave/http/HttpServerParserAdapter.java b/instrumentation/http/src/main/java/brave/http/HttpServerParserAdapter.java index 4e7ae572bc..534dd65cfc 100644 --- a/instrumentation/http/src/main/java/brave/http/HttpServerParserAdapter.java +++ b/instrumentation/http/src/main/java/brave/http/HttpServerParserAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -44,7 +44,7 @@ public void request(HttpAdapter adapter, Req req, SpanCustomizer customizer) { HttpRequest request; if (req instanceof HttpServerRequest) { - request = new HttpServerAdapters.FromRequestAdapter<>((HttpServerAdapter) adapter, req); + request = new HttpServerAdapters.FromRequestAdapter((HttpServerAdapter) adapter, req); } else if (adapter instanceof HttpServerAdapters.ToRequestAdapter) { request = ((HttpServerAdapters.ToRequestAdapter) adapter).delegate; } else { @@ -58,7 +58,7 @@ public void request(HttpAdapter adapter, Req req, SpanCustomizer c HttpResponse response; if (res instanceof HttpServerResponse) { response = - new HttpServerAdapters.FromResponseAdapter<>((HttpServerAdapter) adapter, res, error); + new HttpServerAdapters.FromResponseAdapter((HttpServerAdapter) adapter, res, error); } else if (adapter instanceof HttpServerAdapters.ToResponseAdapter) { response = ((HttpServerAdapters.ToResponseAdapter) adapter).delegate; } else { diff --git a/instrumentation/http/src/main/java/brave/http/HttpTags.java b/instrumentation/http/src/main/java/brave/http/HttpTags.java index 771e7bd7e0..1c96094c62 100644 --- a/instrumentation/http/src/main/java/brave/http/HttpTags.java +++ b/instrumentation/http/src/main/java/brave/http/HttpTags.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -29,7 +29,7 @@ */ public final class HttpTags { /** In worst case, holds 500 strings corresponding to all valid status codes. */ - static final Map CACHED_STATUS_CODES = new ConcurrentHashMap<>(); + static final Map CACHED_STATUS_CODES = new ConcurrentHashMap(); /** * This tags "http.method" as the value of {@link HttpRequest#method()}, such as "GET" or @@ -142,7 +142,7 @@ public static Tag requestHeader(String headerName) { * @see HttpResponse#request() * @since 5.11 */ - public static Tag requestHeader(String key, String headerName) { + public static Tag requestHeader(String key, final String headerName) { return new Tag(key) { String name = validateNonEmpty("headerName", headerName); diff --git a/instrumentation/http/src/main/java/brave/http/HttpTracing.java b/instrumentation/http/src/main/java/brave/http/HttpTracing.java index 77aefdaf66..2b7ccd9d47 100644 --- a/instrumentation/http/src/main/java/brave/http/HttpTracing.java +++ b/instrumentation/http/src/main/java/brave/http/HttpTracing.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -34,7 +34,7 @@ */ // Not final as it previously was not. This allows mocks and similar. public class HttpTracing implements Closeable { - static final AtomicReference CURRENT = new AtomicReference<>(); + static final AtomicReference CURRENT = new AtomicReference(); public static HttpTracing create(Tracing tracing) { return newBuilder(tracing).build(); diff --git a/instrumentation/httpasyncclient/pom.xml b/instrumentation/httpasyncclient/pom.xml index 6f81ee789b..1027031f2b 100644 --- a/instrumentation/httpasyncclient/pom.xml +++ b/instrumentation/httpasyncclient/pom.xml @@ -30,8 +30,6 @@ brave.httpasyncclient ${project.basedir}/../.. - 1.6 - java16 @@ -54,4 +52,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/httpasyncclient/src/main/java/brave/httpasyncclient/TracingHttpAsyncClientBuilder.java b/instrumentation/httpasyncclient/src/main/java/brave/httpasyncclient/TracingHttpAsyncClientBuilder.java index 9c702fdc0a..e1f688c266 100644 --- a/instrumentation/httpasyncclient/src/main/java/brave/httpasyncclient/TracingHttpAsyncClientBuilder.java +++ b/instrumentation/httpasyncclient/src/main/java/brave/httpasyncclient/TracingHttpAsyncClientBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -136,10 +136,10 @@ final class TracingHttpAsyncClient extends CloseableHttpAsyncClient { return delegate.execute( new TracingAsyncRequestProducer(requestProducer, httpContext), - new TracingAsyncResponseConsumer<>(responseConsumer, httpContext), + new TracingAsyncResponseConsumer(responseConsumer, httpContext), httpContext, callback != null && invocationContext != null - ? new TraceContextFutureCallback<>(callback, currentTraceContext, invocationContext) + ? new TraceContextFutureCallback(callback, currentTraceContext, invocationContext) : callback ); } @@ -174,20 +174,29 @@ static final class TraceContextFutureCallback implements FutureCallback { } @Override public void completed(T t) { - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate.completed(t); + } finally { + scope.close(); } } @Override public void failed(Exception e) { - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate.failed(e); + } finally { + scope.close(); } } @Override public void cancelled() { - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate.cancelled(); + } finally { + scope.close(); } } diff --git a/instrumentation/httpclient/pom.xml b/instrumentation/httpclient/pom.xml index 4bc18d8645..f991fdb315 100644 --- a/instrumentation/httpclient/pom.xml +++ b/instrumentation/httpclient/pom.xml @@ -30,8 +30,6 @@ brave.httpclient ${project.basedir}/../.. - 1.6 - java16 4.3.6 @@ -86,16 +84,54 @@ httpclient ${old-httpclient.version} MAIN + jar org.apache.httpcomponents httpclient-cache ${old-httpclient.version} MAIN + jar + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/httpclient/src/main/java/brave/httpclient/TracingProtocolExec.java b/instrumentation/httpclient/src/main/java/brave/httpclient/TracingProtocolExec.java index eae62c2a24..0fefa5834c 100644 --- a/instrumentation/httpclient/src/main/java/brave/httpclient/TracingProtocolExec.java +++ b/instrumentation/httpclient/src/main/java/brave/httpclient/TracingProtocolExec.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -35,6 +35,8 @@ import org.apache.http.conn.routing.HttpRoute; import org.apache.http.impl.execchain.ClientExecChain; +import static brave.internal.Throwables.propagateIfFatal; + /** * protocol exec is the last in the execution chain, so first to execute. We eagerly create a span * here so that user interceptors can see it. @@ -62,13 +64,25 @@ final class TracingProtocolExec implements ClientExecChain { CloseableHttpResponse response = null; Throwable error = null; - try (SpanInScope ws = tracer.withSpanInScope(span)) { + SpanInScope ws = tracer.withSpanInScope(span); + try { return response = protocolExec.execute(route, req, context, execAware); - } catch (Throwable e) { + } catch (RuntimeException e) { + error = e; + throw e; + } catch (HttpException e) { + error = e; + throw e; + } catch (IOException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); error = e; throw e; } finally { handler.handleReceive(new HttpResponseWrapper(response, context, error), span); + ws.close(); } } diff --git a/instrumentation/httpclient5/pom.xml b/instrumentation/httpclient5/pom.xml index 65fb5b149b..2c726caf8c 100644 --- a/instrumentation/httpclient5/pom.xml +++ b/instrumentation/httpclient5/pom.xml @@ -31,8 +31,7 @@ brave.httpclient5 ${project.basedir}/../.. - 1.7 - java17 + 5.2 @@ -62,4 +61,40 @@ test + + + + release + + + 1.7 + java17 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/jaxrs2/pom.xml b/instrumentation/jaxrs2/pom.xml index 388badd41d..bc75bbbe97 100644 --- a/instrumentation/jaxrs2/pom.xml +++ b/instrumentation/jaxrs2/pom.xml @@ -30,8 +30,6 @@ brave.jaxrs2 ${project.basedir}/../.. - 1.6 - java16 @@ -99,4 +97,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/jms-jakarta/pom.xml b/instrumentation/jms-jakarta/pom.xml index c16b0360fe..8a9d56d991 100644 --- a/instrumentation/jms-jakarta/pom.xml +++ b/instrumentation/jms-jakarta/pom.xml @@ -30,8 +30,6 @@ brave.jakarta.jms ${project.basedir}/../.. - 1.6 - java16 -Xep:MissingOverride:OFF diff --git a/instrumentation/jms/pom.xml b/instrumentation/jms/pom.xml index 4afbb8ec2c..aa2231825c 100644 --- a/instrumentation/jms/pom.xml +++ b/instrumentation/jms/pom.xml @@ -30,8 +30,6 @@ brave.jms ${project.basedir}/../.. - 1.6 - java16 -Xep:MissingOverride:OFF @@ -108,4 +106,40 @@ + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/jms/src/main/java/brave/jms/JmsTracing.java b/instrumentation/jms/src/main/java/brave/jms/JmsTracing.java index 13ac3d43ee..ad62b0edfa 100644 --- a/instrumentation/jms/src/main/java/brave/jms/JmsTracing.java +++ b/instrumentation/jms/src/main/java/brave/jms/JmsTracing.java @@ -161,7 +161,7 @@ public JmsTracing build() { this.producerSampler = builder.messagingTracing.producerSampler(); this.consumerSampler = builder.messagingTracing.consumerSampler(); this.remoteServiceName = builder.remoteServiceName; - this.traceIdProperties = new LinkedHashSet<>(propagation.keys()); + this.traceIdProperties = new LinkedHashSet(propagation.keys()); } public Connection connection(Connection connection) { diff --git a/instrumentation/jms/src/main/java/brave/jms/PropertyFilter.java b/instrumentation/jms/src/main/java/brave/jms/PropertyFilter.java index 87c4001ded..3666385171 100644 --- a/instrumentation/jms/src/main/java/brave/jms/PropertyFilter.java +++ b/instrumentation/jms/src/main/java/brave/jms/PropertyFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -86,13 +86,14 @@ static void filterProperties(Message message, Set namesToClear, List> MESSAGE_PROPERTIES_BUFFER = new ThreadLocal<>(); + static final ThreadLocal> MESSAGE_PROPERTIES_BUFFER = + new ThreadLocal>(); /** Also use pair indexing for temporary message properties: (name, value). */ static ArrayList messagePropertiesBuffer() { ArrayList messagePropertiesBuffer = MESSAGE_PROPERTIES_BUFFER.get(); if (messagePropertiesBuffer == null) { - messagePropertiesBuffer = new ArrayList<>(); + messagePropertiesBuffer = new ArrayList(); MESSAGE_PROPERTIES_BUFFER.set(messagePropertiesBuffer); } return messagePropertiesBuffer; diff --git a/instrumentation/jms/src/main/java/brave/jms/TracingCompletionListener.java b/instrumentation/jms/src/main/java/brave/jms/TracingCompletionListener.java index c4e565d8cf..fc84d068d8 100644 --- a/instrumentation/jms/src/main/java/brave/jms/TracingCompletionListener.java +++ b/instrumentation/jms/src/main/java/brave/jms/TracingCompletionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -45,23 +45,27 @@ static CompletionListener create(CompletionListener delegate, } @Override public void onCompletion(Message message) { - try (Scope ws = current.maybeScope(span.context())) { + Scope ws = current.maybeScope(span.context()); + try { delegate.onCompletion(message); } finally { // TODO: in order to tag messageId // parse(new MessageConsumerRequest(message, destination)) span.finish(); + ws.close(); } } @Override public void onException(Message message, Exception exception) { - try (Scope ws = current.maybeScope(span.context())) { + Scope ws = current.maybeScope(span.context()); + try { // TODO: in order to tag messageId // parse(new MessageConsumerRequest(message, destination)) delegate.onException(message, exception); } finally { span.error(exception); span.finish(); + ws.close(); } } } diff --git a/instrumentation/jms/src/main/java/brave/jms/TracingExceptionListener.java b/instrumentation/jms/src/main/java/brave/jms/TracingExceptionListener.java index bd6f22c87e..cd57a71ca0 100644 --- a/instrumentation/jms/src/main/java/brave/jms/TracingExceptionListener.java +++ b/instrumentation/jms/src/main/java/brave/jms/TracingExceptionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -57,10 +57,12 @@ static final class DelegateAndTagError extends TagError { delegate.onException(exception); return; } - try (SpanInScope ws = tracer.withSpanInScope(span)) { + SpanInScope ws = tracer.withSpanInScope(span); + try { delegate.onException(exception); } finally { span.error(exception); + ws.close(); } } } diff --git a/instrumentation/jms/src/main/java/brave/jms/TracingJMSProducer.java b/instrumentation/jms/src/main/java/brave/jms/TracingJMSProducer.java index 4f5210b0fc..53d30d00e0 100644 --- a/instrumentation/jms/src/main/java/brave/jms/TracingJMSProducer.java +++ b/instrumentation/jms/src/main/java/brave/jms/TracingJMSProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -107,10 +107,13 @@ void send(Send send, Destination destination, Object message) { Throwable error = null; try { send.apply(delegate, destination, message); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) { span.error(error).finish(); // An error can happen regardless of async. diff --git a/instrumentation/jms/src/main/java/brave/jms/TracingMessageListener.java b/instrumentation/jms/src/main/java/brave/jms/TracingMessageListener.java index e02c531d33..7c68f20312 100644 --- a/instrumentation/jms/src/main/java/brave/jms/TracingMessageListener.java +++ b/instrumentation/jms/src/main/java/brave/jms/TracingMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -74,10 +74,13 @@ static MessageListener create(MessageListener delegate, JmsTracing jmsTracing) { Throwable error = null; try { delegate.onMessage(message); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) listenerSpan.error(error); listenerSpan.finish(); diff --git a/instrumentation/jms/src/main/java/brave/jms/TracingMessageProducer.java b/instrumentation/jms/src/main/java/brave/jms/TracingMessageProducer.java index d46ca52516..895fd41ba1 100644 --- a/instrumentation/jms/src/main/java/brave/jms/TracingMessageProducer.java +++ b/instrumentation/jms/src/main/java/brave/jms/TracingMessageProducer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -120,10 +120,16 @@ Span createAndStartProducerSpan(Message message, Destination destination) { Throwable error = null; try { delegate.send(message); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error); span.finish(); @@ -138,10 +144,16 @@ Span createAndStartProducerSpan(Message message, Destination destination) { Throwable error = null; try { delegate.send(message, deliveryMode, priority, timeToLive); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error); span.finish(); @@ -184,10 +196,16 @@ void send(SendDestination sendDestination, Destination destination, Message mess Throwable error = null; try { sendDestination.apply(delegate, destination, message); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error); span.finish(); @@ -203,10 +221,16 @@ public void send(Destination destination, Message message, int deliveryMode, int Throwable error = null; try { delegate.send(destination, message, deliveryMode, priority, timeToLive); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error); span.finish(); @@ -223,10 +247,16 @@ public void send(Message message, CompletionListener completionListener) throws Throwable error = null; try { delegate.send(message, TracingCompletionListener.create(completionListener, destination, span, current)); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error).finish(); ws.close(); @@ -243,10 +273,16 @@ public void send(Message message, CompletionListener completionListener) throws Throwable error = null; try { delegate.send(message, deliveryMode, priority, timeToLive, completionListener); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error).finish(); ws.close(); @@ -262,10 +298,16 @@ public void send(Message message, CompletionListener completionListener) throws Throwable error = null; try { delegate.send(destination, message, completionListener); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error).finish(); ws.close(); @@ -281,10 +323,16 @@ public void send(Message message, CompletionListener completionListener) throws Throwable error = null; try { delegate.send(destination, message, deliveryMode, priority, timeToLive, completionListener); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error).finish(); ws.close(); @@ -313,10 +361,16 @@ public void send(Queue queue, Message message, int deliveryMode, int priority, l Throwable error = null; try { qs.send(queue, message, deliveryMode, priority, timeToLive); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error); span.finish(); @@ -346,10 +400,16 @@ void checkQueueSender() { Throwable error = null; try { tp.publish(message); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error); span.finish(); @@ -367,10 +427,16 @@ void checkQueueSender() { Throwable error = null; try { tp.publish(message, deliveryMode, priority, timeToLive); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error); span.finish(); @@ -394,10 +460,16 @@ public void publish(Topic topic, Message message, int deliveryMode, int priority Throwable error = null; try { tp.publish(topic, message, deliveryMode, priority, timeToLive); - } catch (Throwable t) { - propagateIfFatal(t); - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (JMSException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { if (error != null) span.error(error); span.finish(); diff --git a/instrumentation/kafka-clients/pom.xml b/instrumentation/kafka-clients/pom.xml index a31d8f4982..3bdd1ad2fd 100644 --- a/instrumentation/kafka-clients/pom.xml +++ b/instrumentation/kafka-clients/pom.xml @@ -93,15 +93,6 @@ - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - maven-invoker-plugin @@ -124,12 +115,14 @@ kafka-clients ${old-kafka-clients.version} MAIN + jar com.github.charithe kafka-junit ${old-kafka-junit.version} MAIN + jar diff --git a/instrumentation/kafka-streams/pom.xml b/instrumentation/kafka-streams/pom.xml index 8da54c249f..5f78a7cc77 100644 --- a/instrumentation/kafka-streams/pom.xml +++ b/instrumentation/kafka-streams/pom.xml @@ -71,17 +71,4 @@ test - - - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - - - diff --git a/instrumentation/messaging/pom.xml b/instrumentation/messaging/pom.xml index 7cbdd13f50..33fd88056c 100644 --- a/instrumentation/messaging/pom.xml +++ b/instrumentation/messaging/pom.xml @@ -27,8 +27,6 @@ ${project.basedir}/../.. - 1.6 - java16 @@ -67,4 +65,40 @@ + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/messaging/src/main/java/brave/messaging/MessagingRequestMatchers.java b/instrumentation/messaging/src/main/java/brave/messaging/MessagingRequestMatchers.java index 1cdc6e8df0..0ac54e944e 100644 --- a/instrumentation/messaging/src/main/java/brave/messaging/MessagingRequestMatchers.java +++ b/instrumentation/messaging/src/main/java/brave/messaging/MessagingRequestMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -33,7 +33,7 @@ public final class MessagingRequestMatchers { public static Matcher operationEquals(String operation) { if (operation == null) throw new NullPointerException("operation == null"); if (operation.isEmpty()) throw new NullPointerException("operation is empty"); - return new MessagingOperationEquals<>(operation); + return new MessagingOperationEquals(operation); } static final class MessagingOperationEquals @@ -73,7 +73,7 @@ static final class MessagingOperationEquals public static Matcher channelKindEquals(String channelKind) { if (channelKind == null) throw new NullPointerException("channelKind == null"); if (channelKind.isEmpty()) throw new NullPointerException("channelKind is empty"); - return new MessagingChannelKindEquals<>(channelKind); + return new MessagingChannelKindEquals(channelKind); } static final class MessagingChannelKindEquals @@ -113,7 +113,7 @@ static final class MessagingChannelKindEquals public static Matcher channelNameEquals(String channelName) { if (channelName == null) throw new NullPointerException("channelName == null"); if (channelName.isEmpty()) throw new NullPointerException("channelName is empty"); - return new MessagingChannelNameEquals<>(channelName); + return new MessagingChannelNameEquals(channelName); } static final class MessagingChannelNameEquals diff --git a/instrumentation/messaging/src/main/java/brave/messaging/MessagingTracing.java b/instrumentation/messaging/src/main/java/brave/messaging/MessagingTracing.java index 991589d042..814921809a 100644 --- a/instrumentation/messaging/src/main/java/brave/messaging/MessagingTracing.java +++ b/instrumentation/messaging/src/main/java/brave/messaging/MessagingTracing.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -32,7 +32,7 @@ * @since 5.9 */ public class MessagingTracing implements Closeable { - static final AtomicReference CURRENT = new AtomicReference<>(); + static final AtomicReference CURRENT = new AtomicReference(); /** @since 5.9 */ public static MessagingTracing create(Tracing tracing) { diff --git a/instrumentation/mongodb/pom.xml b/instrumentation/mongodb/pom.xml index 29e5281277..bbc4acbc5e 100644 --- a/instrumentation/mongodb/pom.xml +++ b/instrumentation/mongodb/pom.xml @@ -30,9 +30,6 @@ brave.mongodb ${project.basedir}/../.. - - 1.6 - java16 3.12.7 @@ -57,4 +54,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/mongodb/src/main/java/brave/mongodb/TraceMongoCommandListener.java b/instrumentation/mongodb/src/main/java/brave/mongodb/TraceMongoCommandListener.java index 4bdc062270..51e6adfc0d 100644 --- a/instrumentation/mongodb/src/main/java/brave/mongodb/TraceMongoCommandListener.java +++ b/instrumentation/mongodb/src/main/java/brave/mongodb/TraceMongoCommandListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -41,7 +41,7 @@ */ final class TraceMongoCommandListener implements CommandListener { // See https://docs.mongodb.com/manual/reference/command for the command reference - static final Set COMMANDS_WITH_COLLECTION_NAME = new LinkedHashSet<>(Arrays.asList( + static final Set COMMANDS_WITH_COLLECTION_NAME = new LinkedHashSet(Arrays.asList( "aggregate", "count", "distinct", "mapReduce", "geoSearch", "delete", "find", "findAndModify", "insert", "update", "collMod", "compact", "convertToCapped", "create", "createIndexes", "drop", "dropIndexes", "killCursors", "listIndexes", "reIndex")); diff --git a/instrumentation/mysql/pom.xml b/instrumentation/mysql/pom.xml index 9fbeb2d7bc..b3060b39b9 100644 --- a/instrumentation/mysql/pom.xml +++ b/instrumentation/mysql/pom.xml @@ -30,9 +30,6 @@ brave.mysql ${project.basedir}/../.. - - 1.6 - java16 @@ -50,4 +47,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/netty-codec-http/pom.xml b/instrumentation/netty-codec-http/pom.xml index 95ec1e7b82..c32fe756da 100644 --- a/instrumentation/netty-codec-http/pom.xml +++ b/instrumentation/netty-codec-http/pom.xml @@ -30,9 +30,6 @@ brave.netty.http ${project.basedir}/../.. - - 1.6 - java16 @@ -54,4 +51,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/netty-codec-http/src/main/java/brave/netty/http/TracingHttpServerHandler.java b/instrumentation/netty-codec-http/src/main/java/brave/netty/http/TracingHttpServerHandler.java index d9b060db8e..31c4f93632 100644 --- a/instrumentation/netty-codec-http/src/main/java/brave/netty/http/TracingHttpServerHandler.java +++ b/instrumentation/netty-codec-http/src/main/java/brave/netty/http/TracingHttpServerHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -32,6 +32,8 @@ import java.net.InetSocketAddress; import java.net.URI; +import static brave.internal.Throwables.propagateIfFatal; + final class TracingHttpServerHandler extends ChannelDuplexHandler { final CurrentTraceContext currentTraceContext; final HttpServerHandler handler; @@ -59,7 +61,11 @@ final class TracingHttpServerHandler extends ChannelDuplexHandler { Throwable error = null; try { ctx.fireChannelRead(msg); - } catch (Throwable e) { + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); error = e; throw e; } finally { @@ -83,9 +89,13 @@ final class TracingHttpServerHandler extends ChannelDuplexHandler { Throwable error = null; try { ctx.write(msg, prm); - } catch (Throwable t) { - error = t; - throw t; + } catch (RuntimeException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; + throw e; } finally { HttpServerRequest request = ctx.channel().attr(NettyHttpTracing.REQUEST_ATTRIBUTE).get(); handler.handleSend(new HttpResponseWrapper(request, response, error), span); diff --git a/instrumentation/okhttp3/pom.xml b/instrumentation/okhttp3/pom.xml index e6a44a6f43..b1de28b204 100644 --- a/instrumentation/okhttp3/pom.xml +++ b/instrumentation/okhttp3/pom.xml @@ -85,10 +85,47 @@ mockwebserver ${old-okhttp.version} MAIN + jar + + + + release + + + 1.7 + java17 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/p6spy/pom.xml b/instrumentation/p6spy/pom.xml index 83a5b0d9d3..47e73e56a7 100644 --- a/instrumentation/p6spy/pom.xml +++ b/instrumentation/p6spy/pom.xml @@ -30,8 +30,6 @@ brave.p6spy ${project.basedir}/../.. - 1.6 - java16 3.9.1 10.15.2.0 @@ -72,4 +70,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/p6spy/src/main/java/brave/p6spy/TracingP6SpyOptions.java b/instrumentation/p6spy/src/main/java/brave/p6spy/TracingP6SpyOptions.java index bcc5052e71..2177d9a181 100644 --- a/instrumentation/p6spy/src/main/java/brave/p6spy/TracingP6SpyOptions.java +++ b/instrumentation/p6spy/src/main/java/brave/p6spy/TracingP6SpyOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -48,7 +48,7 @@ public void load(Map options) { @Override public Map getDefaults() { - Map allDefaults = new LinkedHashMap<>(super.getDefaults()); + Map allDefaults = new LinkedHashMap(super.getDefaults()); allDefaults.putAll(logLoadableOptions.getDefaults()); allDefaults.put(INCLUDE_PARAMETER_VALUES, Boolean.FALSE.toString()); allDefaults.put(INCLUDE_AFFECTED_ROWS_COUNT, Boolean.FALSE.toString()); diff --git a/instrumentation/rpc/pom.xml b/instrumentation/rpc/pom.xml index f0711d40db..51eda335fb 100644 --- a/instrumentation/rpc/pom.xml +++ b/instrumentation/rpc/pom.xml @@ -30,8 +30,6 @@ brave.rpc ${project.basedir}/../.. - 1.6 - java16 @@ -42,4 +40,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/rpc/src/main/java/brave/rpc/RpcRequestMatchers.java b/instrumentation/rpc/src/main/java/brave/rpc/RpcRequestMatchers.java index e0638af2f6..718efe37cd 100644 --- a/instrumentation/rpc/src/main/java/brave/rpc/RpcRequestMatchers.java +++ b/instrumentation/rpc/src/main/java/brave/rpc/RpcRequestMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -33,7 +33,7 @@ public final class RpcRequestMatchers { public static Matcher methodEquals(String method) { if (method == null) throw new NullPointerException("method == null"); if (method.isEmpty()) throw new NullPointerException("method is empty"); - return new RpcMethodEquals<>(method); + return new RpcMethodEquals(method); } static final class RpcMethodEquals implements Matcher { @@ -72,7 +72,7 @@ static final class RpcMethodEquals implements Matcher Matcher serviceEquals(String service) { if (service == null) throw new NullPointerException("service == null"); if (service.isEmpty()) throw new NullPointerException("service is empty"); - return new RpcServiceEquals<>(service); + return new RpcServiceEquals(service); } static final class RpcServiceEquals implements Matcher { diff --git a/instrumentation/rpc/src/main/java/brave/rpc/RpcTracing.java b/instrumentation/rpc/src/main/java/brave/rpc/RpcTracing.java index abc39e8cb9..3d174daacf 100644 --- a/instrumentation/rpc/src/main/java/brave/rpc/RpcTracing.java +++ b/instrumentation/rpc/src/main/java/brave/rpc/RpcTracing.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -32,7 +32,7 @@ * @since 5.8 */ public class RpcTracing implements Closeable { - static final AtomicReference CURRENT = new AtomicReference<>(); + static final AtomicReference CURRENT = new AtomicReference(); /** @since 5.8 */ public static RpcTracing create(Tracing tracing) { diff --git a/instrumentation/servlet-jakarta/pom.xml b/instrumentation/servlet-jakarta/pom.xml index 83bb0ca7ab..22fbfd60fe 100644 --- a/instrumentation/servlet-jakarta/pom.xml +++ b/instrumentation/servlet-jakarta/pom.xml @@ -30,8 +30,6 @@ brave.jakarta.servlet ${project.basedir}/../.. - 1.6 - java16 diff --git a/instrumentation/servlet/pom.xml b/instrumentation/servlet/pom.xml index efc48183db..8f19d1fb65 100644 --- a/instrumentation/servlet/pom.xml +++ b/instrumentation/servlet/pom.xml @@ -30,8 +30,6 @@ brave.servlet ${project.basedir}/../.. - 1.6 - java16 @@ -86,16 +84,54 @@ servlet-api 2.5 MAIN + jar org.eclipse.jetty jetty-servlet ${jetty-servlet25.version} MAIN + jar + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/servlet/src/main/java/brave/servlet/TracingFilter.java b/instrumentation/servlet/src/main/java/brave/servlet/TracingFilter.java index a486cfbd16..4dd155abde 100644 --- a/instrumentation/servlet/src/main/java/brave/servlet/TracingFilter.java +++ b/instrumentation/servlet/src/main/java/brave/servlet/TracingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -35,6 +35,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import static brave.internal.Throwables.propagateIfFatal; + public final class TracingFilter implements Filter { public static Filter create(Tracing tracing) { return new TracingFilter(HttpTracing.create(tracing)); @@ -85,7 +87,17 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha try { // any downstream code can see Tracer.currentSpan() or use Tracer.currentSpanCustomizer() chain.doFilter(req, res); - } catch (Throwable e) { + } catch (RuntimeException e) { + error = e; + throw e; + } catch (IOException e) { + error = e; + throw e; + } catch (ServletException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); error = e; throw e; } finally { diff --git a/instrumentation/servlet/src/main/java/brave/servlet/internal/ServletRuntime.java b/instrumentation/servlet/src/main/java/brave/servlet/internal/ServletRuntime.java index a9a87dbbda..7fca26afad 100644 --- a/instrumentation/servlet/src/main/java/brave/servlet/internal/ServletRuntime.java +++ b/instrumentation/servlet/src/main/java/brave/servlet/internal/ServletRuntime.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -187,7 +187,7 @@ static final class Servlet25 extends ServletRuntime { // copy-on-write global reflection cache outperforms thread local copies final AtomicReference, Object>> classToGetStatus = - new AtomicReference<>(new LinkedHashMap<>()); + new AtomicReference, Object>>(new LinkedHashMap, Object>()); static final String RETURN_NULL = "RETURN_NULL"; /** @@ -213,14 +213,14 @@ static final class Servlet25 extends ServletRuntime { try { // we don't check for accessibility as isAccessible is deprecated: just fail later getStatusMethod = clazz.getMethod("getStatus"); - return (int) ((Method) getStatusMethod).invoke(response); + return (Integer) ((Method) getStatusMethod).invoke(response); } catch (Throwable throwable) { propagateIfFatal(throwable); getStatusMethod = RETURN_NULL; return 0; } finally { // regardless of success or fail, replace the cache - Map, Object> replacement = new LinkedHashMap<>(classesToCheck); + Map, Object> replacement = new LinkedHashMap, Object>(classesToCheck); replacement.put(clazz, getStatusMethod); classToGetStatus.set(replacement); // lost race will reset, but only up to size - 1 times } @@ -228,10 +228,10 @@ static final class Servlet25 extends ServletRuntime { // if we are here, we have a cached method, that "should" never fail, but we check anyway try { - return (int) ((Method) getStatusMethod).invoke(response); + return (Integer) ((Method) getStatusMethod).invoke(response); } catch (Throwable throwable) { propagateIfFatal(throwable); - Map, Object> replacement = new LinkedHashMap<>(classesToCheck); + Map, Object> replacement = new LinkedHashMap, Object>(classesToCheck); replacement.put(clazz, RETURN_NULL); classToGetStatus.set(replacement); // prefer overriding on failure return 0; diff --git a/instrumentation/sparkjava/pom.xml b/instrumentation/sparkjava/pom.xml index 7247902e56..ccd428d53d 100644 --- a/instrumentation/sparkjava/pom.xml +++ b/instrumentation/sparkjava/pom.xml @@ -55,17 +55,4 @@ - - - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - - - diff --git a/instrumentation/spring-rabbit/pom.xml b/instrumentation/spring-rabbit/pom.xml index 02bbe8eb05..f30ce9f4e2 100644 --- a/instrumentation/spring-rabbit/pom.xml +++ b/instrumentation/spring-rabbit/pom.xml @@ -30,8 +30,6 @@ brave.spring.rabbit ${project.basedir}/../.. - 1.6 - java16 @@ -82,4 +80,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/spring-web/pom.xml b/instrumentation/spring-web/pom.xml index 6fabe6786d..f9e7f656f3 100644 --- a/instrumentation/spring-web/pom.xml +++ b/instrumentation/spring-web/pom.xml @@ -30,8 +30,6 @@ brave.spring.web ${project.basedir}/../.. - 1.6 - java16 @@ -113,10 +111,47 @@ spring-web ${spring.version} MAIN + jar + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/spring-web/src/main/java/brave/spring/web/TraceContextListenableFuture.java b/instrumentation/spring-web/src/main/java/brave/spring/web/TraceContextListenableFuture.java index e90dd5195c..4e2da1591e 100644 --- a/instrumentation/spring-web/src/main/java/brave/spring/web/TraceContextListenableFuture.java +++ b/instrumentation/spring-web/src/main/java/brave/spring/web/TraceContextListenableFuture.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -44,7 +44,7 @@ final class TraceContextListenableFuture implements ListenableFuture { @Override public void addCallback(ListenableFutureCallback callback) { delegate.addCallback(callback != null - ? new TraceContextListenableFutureCallback<>(callback, this) + ? new TraceContextListenableFutureCallback(callback, this) : null ); } @@ -54,7 +54,7 @@ public void addCallback(SuccessCallback successCallback, FailureCallback failureCallback) { delegate.addCallback( successCallback != null - ? new TraceContextSuccessCallback<>(successCallback, this) + ? new TraceContextSuccessCallback(successCallback, this) : null, failureCallback != null ? new TraceContextFailureCallback(failureCallback, this) @@ -104,14 +104,20 @@ static final class TraceContextListenableFutureCallback } @Override public void onSuccess(T result) { - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate.onSuccess(result); + } finally { + scope.close(); } } @Override public void onFailure(Throwable ex) { - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate.onFailure(ex); + } finally { + scope.close(); } } @@ -133,8 +139,11 @@ static final class TraceContextSuccessCallback implements SuccessCallback } @Override public void onSuccess(T result) { - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate.onSuccess(result); + } finally { + scope.close(); } } @@ -156,8 +165,11 @@ static final class TraceContextFailureCallback implements FailureCallback { } @Override public void onFailure(Throwable ex) { - try (Scope scope = currentTraceContext.maybeScope(invocationContext)) { + Scope scope = currentTraceContext.maybeScope(invocationContext); + try { delegate.onFailure(ex); + } finally { + scope.close(); } } diff --git a/instrumentation/spring-web/src/main/java/brave/spring/web/TracingAsyncClientHttpRequestInterceptor.java b/instrumentation/spring-web/src/main/java/brave/spring/web/TracingAsyncClientHttpRequestInterceptor.java index 9f3c23a6f2..c33562851d 100644 --- a/instrumentation/spring-web/src/main/java/brave/spring/web/TracingAsyncClientHttpRequestInterceptor.java +++ b/instrumentation/spring-web/src/main/java/brave/spring/web/TracingAsyncClientHttpRequestInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -33,6 +33,8 @@ import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; +import static brave.internal.Throwables.propagateIfFatal; + public final class TracingAsyncClientHttpRequestInterceptor implements AsyncClientHttpRequestInterceptor { @@ -62,15 +64,29 @@ public static AsyncClientHttpRequestInterceptor create(HttpTracing httpTracing) ? currentTraceContext.get() : null; - try (Scope ws = currentTraceContext.maybeScope(span.context())) { + Scope ws = currentTraceContext.maybeScope(span.context()); + Throwable error = null; + try { ListenableFuture result = execution.executeAsync(req, body); result.addCallback(new TraceListenableFutureCallback(request, span, handler)); return invocationContext != null - ? new TraceContextListenableFuture<>(result, currentTraceContext, invocationContext) + ? new TraceContextListenableFuture(result, currentTraceContext, invocationContext) : result; - } catch (Throwable e) { - handler.handleReceive(new ClientHttpResponseWrapper(request, null, e), span); + } catch (RuntimeException e) { + error = e; + throw e; + } catch (IOException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); + error = e; throw e; + } finally { + if (error != null) { + handler.handleReceive(new ClientHttpResponseWrapper(request, null, error), span); + } + ws.close(); } } diff --git a/instrumentation/spring-web/src/main/java/brave/spring/web/TracingClientHttpRequestInterceptor.java b/instrumentation/spring-web/src/main/java/brave/spring/web/TracingClientHttpRequestInterceptor.java index 03acadab3c..563110d6b1 100644 --- a/instrumentation/spring-web/src/main/java/brave/spring/web/TracingClientHttpRequestInterceptor.java +++ b/instrumentation/spring-web/src/main/java/brave/spring/web/TracingClientHttpRequestInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -29,6 +29,8 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.HttpStatusCodeException; +import static brave.internal.Throwables.propagateIfFatal; + public final class TracingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { public static ClientHttpRequestInterceptor create(Tracing tracing) { return create(HttpTracing.create(tracing)); @@ -51,14 +53,23 @@ public static ClientHttpRequestInterceptor create(HttpTracing httpTracing) { HttpRequestWrapper request = new HttpRequestWrapper(req); Span span = handler.handleSend(request); ClientHttpResponse response = null; + Scope ws = currentTraceContext.newScope(span.context()); Throwable error = null; - try (Scope ws = currentTraceContext.newScope(span.context())) { + try { return response = execution.execute(req, body); - } catch (Throwable e) { + } catch (RuntimeException e) { + error = e; + throw e; + } catch (IOException e) { + error = e; + throw e; + } catch (Error e) { + propagateIfFatal(e); error = e; throw e; } finally { handler.handleReceive(new ClientHttpResponseWrapper(request, response, error), span); + ws.close(); } } diff --git a/instrumentation/spring-webmvc/pom.xml b/instrumentation/spring-webmvc/pom.xml index 644f557fd0..dda19ea5fa 100644 --- a/instrumentation/spring-webmvc/pom.xml +++ b/instrumentation/spring-webmvc/pom.xml @@ -30,8 +30,6 @@ brave.spring.webmvc ${project.basedir}/../.. - 1.6 - java16 @@ -66,27 +64,60 @@ ${jetty.version} test - - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito2 - ${powermock.version} - test - - - - - maven-invoker-plugin - - - + + + + invoker-only-11 + + [11,12) + + + + + maven-invoker-plugin + + + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/WebMvcRuntime.java b/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/WebMvcRuntime.java index be2ac8b506..38777f9b5f 100644 --- a/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/WebMvcRuntime.java +++ b/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/WebMvcRuntime.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -42,7 +42,7 @@ static WebMvcRuntime findWebMvcRuntime() { // Find spring-webmvc v3.1 new methods try { Class.forName("org.springframework.web.method.HandlerMethod"); - return new WebMvc31(); // intentionally doesn't not access the type prior to the above guard + return new WebMvc31(); // intentionally doesn't access the type prior to the above guard } catch (ClassNotFoundException e) { // pre spring-webmvc v3.1 } diff --git a/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/WebMvcRuntimeTest.java b/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/WebMvcRuntimeTest.java index bee2fffa50..5f2541d9ba 100644 --- a/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/WebMvcRuntimeTest.java +++ b/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/WebMvcRuntimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -16,43 +16,29 @@ import brave.http.HttpTracing; import brave.spring.webmvc.WebMvcRuntime.WebMvc25; import brave.spring.webmvc.WebMvcRuntime.WebMvc31; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.web.method.HandlerMethod; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; +import static org.mockito.Mockito.when; -@RunWith(PowerMockRunner.class) -// Added to declutter console: tells power mock not to mess with implicit classes we aren't testing -@PowerMockIgnore({"org.apache.logging.*", "javax.script.*"}) -@PrepareForTest(WebMvcRuntime.class) -public class WebMvcRuntimeTest { +@ExtendWith(MockitoExtension.class) +class WebMvcRuntimeTest { - @Test public void findWebMvcRuntime_HandlerMethod_exists() throws Exception { + @Test void findWebMvcRuntime_HandlerMethod_exists() { assertThat(WebMvcRuntime.findWebMvcRuntime()) .isInstanceOf(WebMvc31.class); } - @Test public void findWebMvcRuntime_HandlerMethod_notFound() throws Exception { - mockStatic(Class.class); - when(Class.forName(HandlerMethod.class.getName())) - .thenThrow(new ClassNotFoundException()); - - assertThat(WebMvcRuntime.findWebMvcRuntime()) - .isInstanceOf(WebMvc25.class); - } - - @Test public void WebMvc31_isHandlerMethod() { + @Test void WebMvc31_isHandlerMethod() { HandlerMethod handlerMethod = mock(HandlerMethod.class); assertThat(new WebMvc31().isHandlerMethod(handlerMethod)) @@ -60,7 +46,7 @@ public class WebMvcRuntimeTest { } /** Due to HandlerMethod being only present after 3.1, we can't look up the class in 2.5 */ - @Test public void WebMvc25_isHandlerMethod_isFalse() { + @Test void WebMvc25_isHandlerMethod_isFalse() { HandlerMethod handlerMethod = mock(HandlerMethod.class); assertThat(new WebMvc25().isHandlerMethod(handlerMethod)) @@ -68,7 +54,7 @@ public class WebMvcRuntimeTest { } /** Spring 3+ can get beans by type, so use that! */ - @Test public void WebMvc31_httpTracing_byType() { + @Test void WebMvc31_httpTracing_byType() { ApplicationContext context = mock(ApplicationContext.class); new WebMvc31().httpTracing(context); @@ -78,7 +64,7 @@ public class WebMvcRuntimeTest { } /** Spring 2.5 cannot get beans by type, so fallback to name */ - @Test public void WebMvc25_httpTracing_byName() { + @Test void WebMvc25_httpTracing_byName() { ApplicationContext context = mock(ApplicationContext.class); when(context.containsBean("httpTracing")).thenReturn(true); when(context.getBean("httpTracing")).thenReturn(mock(HttpTracing.class)); @@ -90,20 +76,20 @@ public class WebMvcRuntimeTest { verifyNoMoreInteractions(context); } - @Test(expected = NoSuchBeanDefinitionException.class) - public void WebMvc25_httpTracing_whenWrongType() { + @Test void WebMvc25_httpTracing_whenWrongType() { ApplicationContext context = mock(ApplicationContext.class); when(context.containsBean("httpTracing")).thenReturn(true); when(context.getBean("httpTracing")).thenReturn("foo"); - new WebMvc25().httpTracing(context); + assertThatThrownBy(() -> new WebMvc25().httpTracing(context)) + .isInstanceOf(NoSuchBeanDefinitionException.class); } - @Test(expected = NoSuchBeanDefinitionException.class) - public void WebMvc25_httpTracing_whenDoesntExist() { + @Test void WebMvc25_httpTracing_whenDoesntExist() { ApplicationContext context = mock(ApplicationContext.class); when(context.containsBean("httpTracing")).thenReturn(false); - new WebMvc25().httpTracing(context); + assertThatThrownBy(() -> new WebMvc25().httpTracing(context)) + .isInstanceOf(NoSuchBeanDefinitionException.class); } } diff --git a/instrumentation/vertx-web/pom.xml b/instrumentation/vertx-web/pom.xml index e6b107f0a3..38147c518d 100644 --- a/instrumentation/vertx-web/pom.xml +++ b/instrumentation/vertx-web/pom.xml @@ -55,18 +55,4 @@ test - - - - - net.orfjackal.retrolambda - retrolambda-maven-plugin - - - none - - - - - diff --git a/mvnw b/mvnw index 41c0f0c23d..8d937f4c14 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven Start Up Batch script +# Apache Maven Wrapper startup batch script, version 3.2.0 # # Required ENV vars: # ------------------ @@ -27,7 +27,6 @@ # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -36,6 +35,10 @@ if [ -z "$MAVEN_SKIP_RC" ] ; then + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi @@ -50,7 +53,7 @@ fi cygwin=false; darwin=false; mingw=false -case "`uname`" in +case "$(uname)" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true @@ -58,9 +61,9 @@ case "`uname`" in # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME else - export JAVA_HOME="/Library/Java/Home" + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME fi fi ;; @@ -68,68 +71,38 @@ esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + JAVA_HOME=$(java-config --jre-home) fi fi -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" fi if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" else - javaExecutable="`readlink -f \"$javaExecutable\"`" + javaExecutable="$(readlink -f "\"$javaExecutable\"")" fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') JAVA_HOME="$javaHome" export JAVA_HOME fi @@ -145,7 +118,7 @@ if [ -z "$JAVACMD" ] ; then JAVACMD="$JAVA_HOME/bin/java" fi else - JAVACMD="`which java`" + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" fi fi @@ -159,12 +132,9 @@ if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { - if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" @@ -180,96 +150,99 @@ find_maven_basedir() { fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` + wdir=$(cd "$wdir/.." || exit 1; pwd) fi # end of workaround done - echo "${basedir}" + printf '%s' "$(cd "$basedir" || exit 1; pwd)" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" fi } -BASE_DIR=`find_maven_basedir "$(pwd)"` +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") if [ -z "$BASE_DIR" ]; then exit 1; fi +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi + log "Couldn't find $wrapperJarPath, downloading it ..." + if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") fi if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" fi elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" fi - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" # For Cygwin, switch paths to Windows format before running javac if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" fi fi fi @@ -278,33 +251,58 @@ fi # End of extension ########################################################################################## -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi fi + MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") fi # Provide a "standardized" way to retrieve the CLI args that will # work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" export MAVEN_CMD_LINE_ARGS WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +# shellcheck disable=SC2086 # safe args exec "$JAVACMD" \ $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 86115719e5..f80fbad3e7 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,182 +1,205 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 2150a116da..ece2b38c9f 100755 --- a/pom.xml +++ b/pom.xml @@ -67,8 +67,8 @@ UTF-8 - 1.7 - java17 + 1.8 + java18 1.8 @@ -120,38 +120,38 @@ 2.9.3 - 4.13.1 - 3.18.1 - 2.0.9 - 3.6.28 + 4.13.2 + 5.10.1 + 3.24.2 + 5.8.0 2.33 - 4.2.4 - 1.15.1 + 4.2.10 + 1.19.3 ${skipTests} - 1.19 + 1.23 1.2.8 - 4.0.rc2 - 5.1.1 - 3.8.1 + 4.3 + 5.1.9 + 3.11.0 - 3.1.2 - 3.0.0-M1 - 3.0.0-M3 + 3.6.1 + 3.1.1 + 3.4.1 - 3.2.0 - 3.0.0-M1 - 3.2.1 - 3.2.0 - 3.2.0 - 3.0.0-M1 - 3.2.4 - 3.2.1 - 3.0.0-M5 - 1.6.8 + 3.4.0 + 3.1.1 + 3.6.0 + 3.6.2 + 3.3.0 + 3.0.1 + 3.5.1 + 3.3.0 + 3.2.2 + 1.6.13 @@ -197,10 +197,24 @@ ${junit.version} test + + org.junit.jupiter + junit-jupiter + ${junit-jupiter.version} + test + org.assertj assertj-core ${assertj.version} + + + + net.bytebuddy + * + + test @@ -233,7 +247,7 @@ org.mockito - mockito-core + mockito-junit-jupiter ${mockito.version} test @@ -242,16 +256,6 @@ - - - io.takari - maven - 0.7.7 - - 3.6.3 - - - de.qaware.maven @@ -278,6 +282,7 @@ error_prone_core ${errorprone.version} MAIN + jar org.apache.maven.surefire @@ -392,12 +397,13 @@ !release - true ${project.build.directory}/local-repo + verify - true ${skipTests} true + -Dorg.slf4j.simpleLogger.defaultLogLevel=WARN @@ -423,24 +429,6 @@ ${maven-help-plugin.version} - - net.orfjackal.retrolambda - retrolambda-maven-plugin - 2.5.7 - - - - process-main - - - ${main.java.version} - true - true - - - - - org.codehaus.mojo animal-sniffer-maven-plugin @@ -516,9 +504,9 @@ - - [11,15) + + [11,12),[17,18),[21,22) @@ -658,9 +646,10 @@ - error-prone + error-prone-11+ - true + + [11,12),[17,18),[21,22) @@ -669,11 +658,8 @@ ${maven-compiler-plugin.version} true - - 1.8 - 1.8 - 1.8 - + ${main.java.version} + ${main.java.version} true true @@ -690,6 +676,17 @@ -XDcompilePolicy=simple -Xplugin:ErrorProne ${errorprone.args} + + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED @@ -727,7 +724,7 @@ maven-gpg-plugin - 1.6 + 3.1.0 sign-artifacts diff --git a/spring-beans/pom.xml b/spring-beans/pom.xml index a08827ee20..b6709fcbd1 100644 --- a/spring-beans/pom.xml +++ b/spring-beans/pom.xml @@ -31,8 +31,6 @@ brave.spring.beans ${project.basedir}/.. - 1.6 - java16 @@ -74,4 +72,40 @@ test + + + + release + + + 1.6 + java16 + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-java + + enforce + + + + + + [11,12) + + + + + + + + + + diff --git a/spring-beans/src/main/java/brave/spring/beans/TracingFactoryBean.java b/spring-beans/src/main/java/brave/spring/beans/TracingFactoryBean.java index a348095846..fe89bafc2e 100644 --- a/spring-beans/src/main/java/brave/spring/beans/TracingFactoryBean.java +++ b/spring-beans/src/main/java/brave/spring/beans/TracingFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 The OpenZipkin Authors + * Copyright 2013-2023 The OpenZipkin Authors * * 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 @@ -38,7 +38,7 @@ public class TracingFactoryBean extends AbstractFactoryBean { String localServiceName; @Deprecated Object localEndpoint, endpoint; // don't pin zipkin class @Deprecated Object spanReporter; // don't pin zipkin class - List spanHandlers = new ArrayList<>(); + List spanHandlers = new ArrayList(); Clock clock; Sampler sampler; @Deprecated ErrorParser errorParser;