diff --git a/all/src/test/java/io/opentelemetry/all/NoGuavaTest.java b/all/src/test/java/io/opentelemetry/all/NoGuavaTest.java
new file mode 100644
index 00000000000..b8d44a75522
--- /dev/null
+++ b/all/src/test/java/io/opentelemetry/all/NoGuavaTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.all;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
+
+import com.tngtech.archunit.core.importer.ClassFileImporter;
+import io.opentelemetry.exporter.sender.grpc.managedchannel.internal.UpstreamGrpcSender;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.jar.JarFile;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Prevents Guava API usage in published code, even when Guava is available transitively.
+ *
+ *
This replaces the narrower Guava beta-API check, whose annotation inspection fails with javac
+ * 25 and {@code --release 8}. Only published JARs are scanned: tests may use Guava, and transitive
+ * dependencies are not prohibited.
+ */
+class NoGuavaTest {
+
+ @ParameterizedTest
+ @MethodSource("artifactsAndJars")
+ void noGuavaReferences(String artifactId, String absolutePath) throws IOException {
+ try (JarFile jarFile = new JarFile(new File(absolutePath))) {
+ noClasses()
+ .that()
+ // futureUnaryCall exposes ListenableFuture. Retain its existing completion semantics.
+ .doNotBelongToAnyOf(UpstreamGrpcSender.class)
+ .and()
+ // Generated into main alongside the test server API; this unused stub returns
+ // ListenableFuture. Moving generation to test-only sources is a separate cleanup.
+ .doNotHaveFullyQualifiedName(
+ "io.opentelemetry.sdk.extension.trace.jaeger.proto.api_v2."
+ + "SamplingManagerGrpc$SamplingManagerFutureStub")
+ .should()
+ .dependOnClassesThat()
+ .resideInAPackage("com.google.common..")
+ .as(artifactId + " should not reference Guava except in gRPC future adapters")
+ .check(new ClassFileImporter().importJar(jarFile));
+ }
+ }
+
+ private static Stream artifactsAndJars() throws IOException {
+ return Files.readAllLines(Path.of(System.getenv("ARTIFACTS_AND_JARS"))).stream()
+ .map(
+ line -> {
+ String[] parts = line.split(":", 2);
+ return Arguments.argumentSet(parts[0], parts[0], parts[1]);
+ });
+ }
+}
diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts
index 2d66515256b..d4077f065ae 100644
--- a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts
@@ -37,7 +37,7 @@ tasks.withType().configureEach {
java {
toolchain {
- languageVersion.set(JavaLanguageVersion.of(21))
+ languageVersion.set(JavaLanguageVersion.of(25))
}
withJavadocJar()
@@ -298,8 +298,6 @@ dependencies {
compileOnly("com.google.auto.value:auto-value-annotations")
compileOnly("com.google.code.findbugs:jsr305")
- annotationProcessor("com.google.guava:guava-beta-checker")
-
// Workaround for @javax.annotation.Generated
// see: https://github.com/grpc/grpc-java/issues/3633
compileOnly("javax.annotation:javax.annotation-api")
diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts
index ade32e5eca6..9d1e8fb0f23 100644
--- a/dependencyManagement/build.gradle.kts
+++ b/dependencyManagement/build.gradle.kts
@@ -77,7 +77,6 @@ val DEPENDENCIES = listOf(
"com.github.stefanbirkner:system-rules:1.19.0",
"com.google.api.grpc:proto-google-common-protos:2.78.0",
"com.google.code.findbugs:jsr305:3.0.2",
- "com.google.guava:guava-beta-checker:1.0",
"com.sun.net.httpserver:http:20070405",
"com.squareup.okhttp3:okhttp:$okhttpVersion",
"com.tngtech.archunit:archunit-junit5:1.5.1",
diff --git a/docs/knowledge/build.md b/docs/knowledge/build.md
index b8e079fd06c..c309cf2c8bb 100644
--- a/docs/knowledge/build.md
+++ b/docs/knowledge/build.md
@@ -1,6 +1,6 @@
# Build
-Java 21 or higher is required to build. The built artifacts target Java 8 or higher.
+Java 25 or higher is required to build. The built artifacts target Java 8 or higher.
## Common tasks
diff --git a/exporters/common/build.gradle.kts b/exporters/common/build.gradle.kts
index d0f7ce84a3d..149a5633855 100644
--- a/exporters/common/build.gradle.kts
+++ b/exporters/common/build.gradle.kts
@@ -8,7 +8,7 @@ plugins {
description = "OpenTelemetry Exporter Common"
otelJava.moduleName.set("io.opentelemetry.exporter.internal")
otelJava.requireSuppressWarningsExplanation.set(false)
-otelJava.osgiOptionalPackages.set(listOf("com.google.common.io", "io.opentelemetry.api.incubator.config", "io.opentelemetry.sdk.autoconfigure.spi"))
+otelJava.osgiOptionalPackages.set(listOf("io.opentelemetry.api.incubator.config", "io.opentelemetry.sdk.autoconfigure.spi"))
// sun.misc, io.grpc, and org.jspecify are not OSGi bundles and have no package versioning; must use unversioned optional.
otelJava.osgiUnversionedOptionalPackages.set(listOf("sun.misc", "io.grpc", "org.jspecify.annotations"))
// This bundle's exporters load sender implementations via SPI.
diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/MarshalerInputStream.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/MarshalerInputStream.java
index ea9e98ba0b3..49b3916cc55 100644
--- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/MarshalerInputStream.java
+++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/MarshalerInputStream.java
@@ -22,7 +22,6 @@
package io.opentelemetry.exporter.internal.grpc;
-import com.google.common.io.ByteStreams;
import io.grpc.Drainable;
import io.grpc.KnownLength;
import io.opentelemetry.sdk.common.export.MessageWriter;
@@ -59,7 +58,13 @@ public int drainTo(OutputStream target) throws IOException {
message.writeMessage(target);
message = null;
} else if (partial != null) {
- written = (int) ByteStreams.copy(partial, target);
+ byte[] buffer = new byte[8192];
+ written = 0;
+ int read;
+ while ((read = partial.read(buffer)) != -1) {
+ target.write(buffer, 0, read);
+ written += read;
+ }
partial = null;
} else {
written = 0;
diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/MarshalerInputStreamTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/MarshalerInputStreamTest.java
index 0893577a3b8..f70277b783d 100644
--- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/MarshalerInputStreamTest.java
+++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/MarshalerInputStreamTest.java
@@ -14,7 +14,11 @@
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Random;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
class MarshalerInputStreamTest {
@@ -50,15 +54,32 @@ void drainTo_fromMessage() throws IOException {
assertThat(baos.toByteArray()).isEqualTo(CONTENT);
}
+ @ParameterizedTest
+ @ValueSource(ints = {0, 1, 8192, 8193, 20000})
+ void drainTo_fromPartial(int size) throws IOException {
+ byte[] content = new byte[size];
+ new Random(0).nextBytes(content);
+ MarshalerInputStream stream = streamWithPartial(content);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int written = stream.drainTo(baos);
+
+ assertThat(written).isEqualTo(content.length);
+ assertThat(baos.toByteArray()).isEqualTo(content);
+ assertThat(stream.available()).isZero();
+ assertThat(stream.read()).isEqualTo(-1);
+ }
+
@Test
- void drainTo_fromPartial() throws IOException {
+ void drainTo_afterPartialRead() throws IOException {
MarshalerInputStream stream = streamWithPartial(CONTENT);
+ assertThat(stream.read()).isEqualTo(CONTENT[0] & 0xFF);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int written = stream.drainTo(baos);
- assertThat(written).isEqualTo(CONTENT.length);
- assertThat(baos.toByteArray()).isEqualTo(CONTENT);
+ assertThat(written).isEqualTo(CONTENT.length - 1);
+ assertThat(baos.toByteArray()).isEqualTo(Arrays.copyOfRange(CONTENT, 1, CONTENT.length));
}
@Test
diff --git a/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetryNoRecordEventsSpanImpl.java b/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetryNoRecordEventsSpanImpl.java
index 8ec2fc6296e..5c075c212b2 100644
--- a/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetryNoRecordEventsSpanImpl.java
+++ b/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetryNoRecordEventsSpanImpl.java
@@ -22,7 +22,6 @@
package io.opentelemetry.opencensusshim;
-import com.google.common.base.Preconditions;
import io.opencensus.trace.Annotation;
import io.opencensus.trace.AttributeValue;
import io.opencensus.trace.EndSpanOptions;
@@ -37,6 +36,7 @@
import io.opentelemetry.api.trace.StatusCode;
import java.util.EnumSet;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
@@ -55,39 +55,39 @@ static OpenTelemetryNoRecordEventsSpanImpl create(SpanContext context) {
@Override
public void addAnnotation(String description, Map attributes) {
- Preconditions.checkNotNull(description, "description");
- Preconditions.checkNotNull(attributes, "attribute");
+ Objects.requireNonNull(description, "description");
+ Objects.requireNonNull(attributes, "attribute");
}
@Override
public void addAnnotation(Annotation annotation) {
- Preconditions.checkNotNull(annotation, "annotation");
+ Objects.requireNonNull(annotation, "annotation");
}
@Override
public void putAttribute(String key, AttributeValue value) {
- Preconditions.checkNotNull(key, "key");
- Preconditions.checkNotNull(value, "value");
+ Objects.requireNonNull(key, "key");
+ Objects.requireNonNull(value, "value");
}
@Override
public void putAttributes(Map attributes) {
- Preconditions.checkNotNull(attributes, "attributes");
+ Objects.requireNonNull(attributes, "attributes");
}
@Override
public void addMessageEvent(MessageEvent messageEvent) {
- Preconditions.checkNotNull(messageEvent, "messageEvent");
+ Objects.requireNonNull(messageEvent, "messageEvent");
}
@Override
public void addLink(Link link) {
- Preconditions.checkNotNull(link, "link");
+ Objects.requireNonNull(link, "link");
}
@Override
public void setStatus(Status status) {
- Preconditions.checkNotNull(status, "status");
+ Objects.requireNonNull(status, "status");
}
@Override
@@ -102,7 +102,7 @@ public io.opentelemetry.api.trace.Span setStatus(StatusCode canonicalCode, Strin
@Override
public void end(EndSpanOptions options) {
- Preconditions.checkNotNull(options, "options");
+ Objects.requireNonNull(options, "options");
}
@Override
diff --git a/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanBuilderImpl.java b/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanBuilderImpl.java
index 27f4a19a927..cae916eca8b 100644
--- a/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanBuilderImpl.java
+++ b/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanBuilderImpl.java
@@ -22,8 +22,8 @@
package io.opentelemetry.opencensusshim;
-import static com.google.common.base.Preconditions.checkNotNull;
import static io.opentelemetry.opencensusshim.SpanConverter.mapKind;
+import static java.util.Objects.requireNonNull;
import io.opencensus.implcore.trace.internal.RandomHandler;
import io.opencensus.trace.Sampler;
@@ -73,7 +73,7 @@ private OpenTelemetrySpanBuilderImpl(
@Nullable SpanContext ocRemoteParentSpanContext,
@Nullable Span ocParent,
OpenTelemetrySpanBuilderImpl.Options options) {
- this.name = checkNotNull(name, "name");
+ this.name = requireNonNull(name, "name");
this.ocParent = ocParent;
this.ocRemoteParentSpanContext = ocRemoteParentSpanContext;
this.options = options;
@@ -93,13 +93,13 @@ static OpenTelemetrySpanBuilderImpl createWithRemoteParent(
@Override
public SpanBuilder setSampler(Sampler sampler) {
- this.ocSampler = checkNotNull(sampler, "sampler");
+ this.ocSampler = requireNonNull(sampler, "sampler");
return this;
}
@Override
public SpanBuilder setParentLinks(List parentLinks) {
- this.ocParentLinks = checkNotNull(parentLinks, "parentLinks");
+ this.ocParentLinks = requireNonNull(parentLinks, "parentLinks");
for (Span parent : parentLinks) {
this.otelParentLinks.add(SpanConverter.mapSpanContext(parent.getContext()));
}
@@ -220,8 +220,8 @@ static final class Options {
private final TraceConfig traceConfig;
Options(RandomHandler randomHandler, TraceConfig traceConfig) {
- this.randomHandler = checkNotNull(randomHandler, "randomHandler");
- this.traceConfig = checkNotNull(traceConfig, "traceConfig");
+ this.randomHandler = requireNonNull(randomHandler, "randomHandler");
+ this.traceConfig = requireNonNull(traceConfig, "traceConfig");
}
}
}
diff --git a/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanImpl.java b/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanImpl.java
index 5e4c76a386b..64ea4f1a861 100644
--- a/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanImpl.java
+++ b/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanImpl.java
@@ -31,7 +31,6 @@
import static io.opentelemetry.opencensusshim.SpanConverter.setLongAttribute;
import static io.opentelemetry.opencensusshim.SpanConverter.setStringAttribute;
-import com.google.common.base.Preconditions;
import io.opencensus.trace.Annotation;
import io.opencensus.trace.AttributeValue;
import io.opencensus.trace.EndSpanOptions;
@@ -48,6 +47,7 @@
import io.opentelemetry.api.trace.StatusCode;
import java.util.EnumSet;
import java.util.Map;
+import java.util.Objects;
class OpenTelemetrySpanImpl extends Span
implements io.opentelemetry.api.trace.Span, DelegatingSpan {
@@ -73,8 +73,8 @@ public io.opentelemetry.api.trace.Span getDelegate() {
@Override
public void putAttribute(String key, AttributeValue value) {
- Preconditions.checkNotNull(key, "key");
- Preconditions.checkNotNull(value, "value");
+ Objects.requireNonNull(key, "key");
+ Objects.requireNonNull(value, "value");
value.match(
arg -> DelegatingSpan.super.setAttribute(key, arg),
arg -> DelegatingSpan.super.setAttribute(key, arg),
@@ -85,7 +85,7 @@ public void putAttribute(String key, AttributeValue value) {
@Override
public void putAttributes(Map attributes) {
- Preconditions.checkNotNull(attributes, "attributes");
+ Objects.requireNonNull(attributes, "attributes");
attributes.forEach(this::putAttribute);
}
@@ -105,7 +105,7 @@ public void addAnnotation(Annotation annotation) {
@Override
public void addLink(Link link) {
- Preconditions.checkNotNull(link, "link");
+ Objects.requireNonNull(link, "link");
AttributesBuilder attributesBuilder = Attributes.builder();
mapAttributes(link.getAttributes(), attributesBuilder);
// DelegatingSpan does not override addLink, so DelegatingSpan.super.addLink(..) would call the
@@ -136,7 +136,7 @@ public void addMessageEvent(MessageEvent messageEvent) {
@Override
public void setStatus(Status status) {
- Preconditions.checkNotNull(status, "status");
+ Objects.requireNonNull(status, "status");
StatusCode code = status.isOk() ? StatusCode.OK : StatusCode.ERROR;
String description = status.getDescription();
if (description == null) {