Skip to content

Commit 0e03654

Browse files
committed
core: Remove temporary AbstractManagedChannelImplBuilder
This breaks the ABI of the classes listed below. Users that recompiled their code using grpc-java [`v1.36.0`] (https://github.com/grpc/grpc-java/releases/tag/v1.36.0) (released on Feb 23, 2021) and later, ARE NOT AFFECTED. Users that compiled their source using grpc-java earlier than [`v1.36.0`] (https://github.com/grpc/grpc-java/releases/tag/v1.36.0) need to recompile when upgrading to grpc-java `v1.59.0`. Otherwise the code will fail on runtime with `NoSuchMethodError`. For example, code: ```java NettyChannelBuilder.forTarget("localhost:100").maxRetryAttempts(2); ``` Will fail with > `java.lang.NoSuchMethodError: 'io.grpc.internal.AbstractManagedChannelImplBuilder io.grpc.netty.NettyChannelBuilder.maxRetryAttempts(int)'` **Affected classes** Class `AbstractManagedChannelImplBuilder` is deleted, and no longer in the class hierarchy of the channel builders: - `io.grpc.netty.NettyChannelBuilder` - `io.grpc.okhttp.OkhttpChannelBuilder` - `grpc.cronet.CronetChannelBuilder`
1 parent 050ae18 commit 0e03654

File tree

9 files changed

+53
-175
lines changed

9 files changed

+53
-175
lines changed

api/src/main/java/io/grpc/ForwardingChannelBuilder.java

+11-39
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package io.grpc;
1818

19-
import com.google.common.base.MoreObjects;
20-
import com.google.errorprone.annotations.DoNotCall;
2119
import java.util.List;
2220
import java.util.Map;
2321
import java.util.concurrent.Executor;
@@ -28,40 +26,27 @@
2826
* A {@link ManagedChannelBuilder} that delegates all its builder methods to another builder by
2927
* default.
3028
*
31-
* @param <T> The type of the subclass extending this abstract class.
29+
* <p>Important! Use {@link ForwardingChannelBuilder2} instead!
30+
*
31+
* <p>This class mistakenly used {@code <T extends ForwardingChannelBuilder<T>>} which causes
32+
* return types to be {@link ForwardingChannelBuilder} instead of {@link ManagedChannelBuilder}.
33+
* This pollutes the ABI of its subclasses with undesired method signatures.
34+
* {@link ForwardingChannelBuilder2} generates correct return types; use it instead.
3235
*
36+
* @param <T> The type of the subclass extending this abstract class.
3337
* @since 1.7.0
3438
*/
3539
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/3363")
3640
public abstract class ForwardingChannelBuilder<T extends ForwardingChannelBuilder<T>>
37-
extends ManagedChannelBuilder<T> {
41+
extends ForwardingChannelBuilder2<T> {
42+
// TODO(sergiitk): deprecate after stabilizing
3843

3944
/**
4045
* The default constructor.
4146
*/
42-
protected ForwardingChannelBuilder() {}
43-
44-
/**
45-
* This method serves to force sub classes to "hide" this static factory.
46-
*/
47-
@DoNotCall("Unsupported")
48-
public static ManagedChannelBuilder<?> forAddress(String name, int port) {
49-
throw new UnsupportedOperationException("Subclass failed to hide static factory");
47+
protected ForwardingChannelBuilder() {
5048
}
5149

52-
/**
53-
* This method serves to force sub classes to "hide" this static factory.
54-
*/
55-
@DoNotCall("Unsupported")
56-
public static ManagedChannelBuilder<?> forTarget(String target) {
57-
throw new UnsupportedOperationException("Subclass failed to hide static factory");
58-
}
59-
60-
/**
61-
* Returns the delegated {@code ManagedChannelBuilder}.
62-
*/
63-
protected abstract ManagedChannelBuilder<?> delegate();
64-
6550
@Override
6651
public T directExecutor() {
6752
delegate().directExecutor();
@@ -249,24 +234,11 @@ public T disableServiceConfigLookUp() {
249234
return thisT();
250235
}
251236

252-
/**
253-
* Returns the {@link ManagedChannel} built by the delegate by default. Overriding method can
254-
* return different value.
255-
*/
256-
@Override
257-
public ManagedChannel build() {
258-
return delegate().build();
259-
}
260-
261-
@Override
262-
public String toString() {
263-
return MoreObjects.toStringHelper(this).add("delegate", delegate()).toString();
264-
}
265-
266237
/**
267238
* Returns the correctly typed version of the builder.
268239
*/
269240
protected final T thisT() {
241+
// TODO(sergiitk): make private when stabilizing.
270242
@SuppressWarnings("unchecked")
271243
T thisT = (T) this;
272244
return thisT;

core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java renamed to api/src/main/java/io/grpc/ForwardingChannelBuilder2.java

+18-54
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020 The gRPC Authors
2+
* Copyright 2023 The gRPC Authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,58 +14,46 @@
1414
* limitations under the License.
1515
*/
1616

17-
package io.grpc.internal;
17+
package io.grpc;
1818

1919
import com.google.common.base.MoreObjects;
20-
import com.google.common.base.Preconditions;
2120
import com.google.errorprone.annotations.DoNotCall;
22-
import io.grpc.BinaryLog;
23-
import io.grpc.ClientInterceptor;
24-
import io.grpc.CompressorRegistry;
25-
import io.grpc.DecompressorRegistry;
26-
import io.grpc.ManagedChannel;
27-
import io.grpc.ManagedChannelBuilder;
28-
import io.grpc.NameResolver;
29-
import io.grpc.ProxyDetector;
3021
import java.util.List;
3122
import java.util.Map;
3223
import java.util.concurrent.Executor;
3324
import java.util.concurrent.TimeUnit;
3425
import javax.annotation.Nullable;
3526

3627
/**
37-
* Temporarily duplicates {@link io.grpc.ForwardingChannelBuilder} to fix ABI backward
38-
* compatibility.
28+
* A {@link ManagedChannelBuilder} that delegates all its builder methods to another builder by
29+
* default.
3930
*
40-
* @param <T> The concrete type of this builder.
41-
* @see <a href="https://github.com/grpc/grpc-java/issues/7211">grpc/grpc-java#7211</a>
31+
* <p>Always choose this over {@link ForwardingChannelBuilder}, because
32+
* {@link ForwardingChannelBuilder2} is ABI-safe.
33+
*
34+
* @param <T> The type of the subclass extending this abstract class.
35+
* @since 1.59.0
4236
*/
43-
public abstract class AbstractManagedChannelImplBuilder
44-
<T extends AbstractManagedChannelImplBuilder<T>> extends ManagedChannelBuilder<T> {
45-
46-
/**
47-
* Added for ABI compatibility.
48-
*
49-
* <p>See details in {@link #maxInboundMessageSize(int)}.
50-
* TODO(sergiitk): move back to concrete classes as a private field, when this class is removed.
51-
*/
52-
protected int maxInboundMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
37+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/10585")
38+
public abstract class ForwardingChannelBuilder2<T extends ManagedChannelBuilder<T>>
39+
extends ManagedChannelBuilder<T> {
5340

5441
/**
5542
* The default constructor.
5643
*/
57-
protected AbstractManagedChannelImplBuilder() {}
44+
protected ForwardingChannelBuilder2() {
45+
}
5846

5947
/**
60-
* This method serves to force sub classes to "hide" this static factory.
48+
* This method serves to force subclasses to "hide" this static factory.
6149
*/
6250
@DoNotCall("Unsupported")
6351
public static ManagedChannelBuilder<?> forAddress(String name, int port) {
6452
throw new UnsupportedOperationException("Subclass failed to hide static factory");
6553
}
6654

6755
/**
68-
* This method serves to force sub classes to "hide" this static factory.
56+
* This method serves to force subclasses to "hide" this static factory.
6957
*/
7058
@DoNotCall("Unsupported")
7159
public static ManagedChannelBuilder<?> forTarget(String target) {
@@ -170,31 +158,7 @@ public T idleTimeout(long value, TimeUnit unit) {
170158

171159
@Override
172160
public T maxInboundMessageSize(int max) {
173-
/*
174-
Why this method is not delegating, as the rest of the methods?
175-
176-
In refactoring described in #7211, the implementation of #maxInboundMessageSize(int)
177-
(and its corresponding field) was pulled down from internal AbstractManagedChannelImplBuilder
178-
to concrete classes that actually enforce this setting. For the same reason, it wasn't ported
179-
to ManagedChannelImplBuilder (the #delegate()).
180-
181-
Then AbstractManagedChannelImplBuilder was brought back to fix ABI backward compatibility,
182-
and temporarily turned into a ForwardingChannelBuilder, ref PR #7564. Eventually it will
183-
be deleted, after a period with "bridge" ABI solution introduced in #7834.
184-
185-
However, restoring AbstractManagedChannelImplBuilder unintentionally made ABI of
186-
#maxInboundMessageSize(int) implemented by the concrete classes backward incompatible:
187-
pre-refactoring builds expect it to be a method of AbstractManagedChannelImplBuilder,
188-
and not concrete classes, ref #8313.
189-
190-
The end goal is to keep #maxInboundMessageSize(int) only in concrete classes that enforce it.
191-
To fix method's ABI, we temporary reintroduce it to the original layer it was removed from:
192-
AbstractManagedChannelImplBuilder. This class' only intention is to provide short-term
193-
ABI compatibility. Once we move forward with dropping the ABI, both fixes are no longer
194-
necessary, and both will perish with removing AbstractManagedChannelImplBuilder.
195-
*/
196-
Preconditions.checkArgument(max >= 0, "negative max");
197-
maxInboundMessageSize = max;
161+
delegate().maxInboundMessageSize(max);
198162
return thisT();
199163
}
200164

@@ -305,7 +269,7 @@ public String toString() {
305269
/**
306270
* Returns the correctly typed version of the builder.
307271
*/
308-
protected final T thisT() {
272+
private T thisT() {
309273
@SuppressWarnings("unchecked")
310274
T thisT = (T) this;
311275
return thisT;

core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java renamed to api/src/test/java/io/grpc/ForwardingChannelBuilder2Test.java

+8-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020 The gRPC Authors
2+
* Copyright 2023 The gRPC Authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,33 +14,30 @@
1414
* limitations under the License.
1515
*/
1616

17-
package io.grpc.internal;
17+
package io.grpc;
1818

1919
import static com.google.common.truth.Truth.assertThat;
2020
import static org.mockito.Mockito.doReturn;
2121
import static org.mockito.Mockito.mock;
2222

2323
import com.google.common.base.Defaults;
24-
import com.google.common.collect.ImmutableSet;
25-
import io.grpc.ForwardingTestUtil;
26-
import io.grpc.ManagedChannel;
27-
import io.grpc.ManagedChannelBuilder;
2824
import java.lang.reflect.Method;
2925
import java.lang.reflect.Modifier;
26+
import java.util.Collections;
3027
import org.junit.Test;
3128
import org.junit.runner.RunWith;
3229
import org.junit.runners.JUnit4;
3330

3431
/**
35-
* Unit tests for {@link AbstractManagedChannelImplBuilder}.
32+
* Unit tests for {@link ForwardingChannelBuilder2}.
3633
*/
3734
@RunWith(JUnit4.class)
38-
public class AbstractManagedChannelImplBuilderTest {
35+
public class ForwardingChannelBuilder2Test {
3936
private final ManagedChannelBuilder<?> mockDelegate = mock(ManagedChannelBuilder.class);
4037

41-
private final AbstractManagedChannelImplBuilder<?> testChannelBuilder = new TestBuilder();
38+
private final ForwardingChannelBuilder2<?> testChannelBuilder = new TestBuilder();
4239

43-
private final class TestBuilder extends AbstractManagedChannelImplBuilder<TestBuilder> {
40+
private final class TestBuilder extends ForwardingChannelBuilder2<TestBuilder> {
4441
@Override
4542
protected ManagedChannelBuilder<?> delegate() {
4643
return mockDelegate;
@@ -53,8 +50,7 @@ public void allMethodsForwarded() throws Exception {
5350
ManagedChannelBuilder.class,
5451
mockDelegate,
5552
testChannelBuilder,
56-
// maxInboundMessageSize is the only method that shouldn't forward.
57-
ImmutableSet.of(ManagedChannelBuilder.class.getMethod("maxInboundMessageSize", int.class)),
53+
Collections.emptyList(),
5854
new ForwardingTestUtil.ArgumentProvider() {
5955
@Override
6056
public Object get(Method method, int argPos, Class<?> clazz) {
@@ -67,15 +63,6 @@ public Object get(Method method, int argPos, Class<?> clazz) {
6763
});
6864
}
6965

70-
@Test
71-
public void testMaxInboundMessageSize() {
72-
assertThat(testChannelBuilder.maxInboundMessageSize)
73-
.isEqualTo(GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE);
74-
75-
testChannelBuilder.maxInboundMessageSize(42);
76-
assertThat(testChannelBuilder.maxInboundMessageSize).isEqualTo(42);
77-
}
78-
7966
@Test
8067
public void allBuilderMethodsReturnThis() throws Exception {
8168
for (Method method : ManagedChannelBuilder.class.getDeclaredMethods()) {

core/build.gradle

-48
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ plugins {
1414
id "ru.vyarus.animalsniffer"
1515
}
1616

17-
import static java.nio.charset.StandardCharsets.US_ASCII;
18-
19-
import com.google.common.primitives.Bytes;
20-
2117
description = 'gRPC: Core'
2218

2319
dependencies {
@@ -73,50 +69,6 @@ animalsniffer {
7369
components.java.withVariantsFromConfiguration(configurations.testFixturesApiElements) { skip() }
7470
components.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() }
7571

76-
import net.ltgt.gradle.errorprone.CheckSeverity
77-
78-
def replaceBytes(byte[] haystack, byte[] needle, byte[] replacement) {
79-
int i = Bytes.indexOf(haystack, needle);
80-
assert i != -1;
81-
byte[] result = new byte[haystack.length - needle.length + replacement.length];
82-
System.arraycopy(haystack, 0, result, 0, i);
83-
System.arraycopy(replacement, 0, result, i, replacement.length);
84-
System.arraycopy(haystack, i + needle.length, result, i + replacement.length, haystack.length - i - needle.length);
85-
return result;
86-
}
87-
88-
def bigEndianShortBytes(int value) {
89-
return [value >> 8, value & 0xFF] as byte[];
90-
}
91-
92-
def replaceConstant(File file, String needle, String replacement) {
93-
// CONSTANT_Utf8_info. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.7
94-
byte[] needleBytes = Bytes.concat(
95-
[1] as byte[], bigEndianShortBytes(needle.length()), needle.getBytes(US_ASCII));
96-
byte[] replacementBytes = Bytes.concat(
97-
[1] as byte[], bigEndianShortBytes(replacement.length()), replacement.getBytes(US_ASCII));
98-
file.setBytes(replaceBytes(file.getBytes(), needleBytes, replacementBytes));
99-
}
100-
101-
plugins.withId("java") {
102-
tasks.named("compileJava").configure {
103-
doLast {
104-
// Replace value of Signature Attribute.
105-
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.9
106-
//
107-
// Have new compilations compile against a public class, without breaking the internal
108-
// ABI for the moment. After giving users some time to recompile, this should be removed
109-
// and #7211 can continue. When this is removed, at the very least the generics need to
110-
// be changed to avoid <? extends io.grpc.internal.*>.
111-
project.replaceConstant(
112-
destinationDirectory.file(
113-
"io/grpc/internal/AbstractManagedChannelImplBuilder.class").get().getAsFile(),
114-
"<T:Lio/grpc/internal/AbstractManagedChannelImplBuilder<TT;>;>Lio/grpc/ManagedChannelBuilder<TT;>;",
115-
"<T:Lio/grpc/ManagedChannelBuilder<TT;>;>Lio/grpc/ManagedChannelBuilder<TT;>;");
116-
}
117-
}
118-
}
119-
12072
tasks.register("versionFile") {
12173
doLast {
12274
new File(buildDir, "version").write("${project.version}\n")

cronet/src/main/java/io/grpc/cronet/CronetChannelBuilder.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
import io.grpc.ChannelCredentials;
2929
import io.grpc.ChannelLogger;
3030
import io.grpc.ExperimentalApi;
31+
import io.grpc.ForwardingChannelBuilder2;
3132
import io.grpc.Internal;
3233
import io.grpc.ManagedChannelBuilder;
33-
import io.grpc.internal.AbstractManagedChannelImplBuilder;
3434
import io.grpc.internal.ClientTransportFactory;
3535
import io.grpc.internal.ConnectionClientTransport;
3636
import io.grpc.internal.GrpcUtil;
@@ -52,8 +52,7 @@
5252

5353
/** Convenience class for building channels with the cronet transport. */
5454
@ExperimentalApi("There is no plan to make this API stable, given transport API instability")
55-
public final class CronetChannelBuilder
56-
extends AbstractManagedChannelImplBuilder<CronetChannelBuilder> {
55+
public final class CronetChannelBuilder extends ForwardingChannelBuilder2<CronetChannelBuilder> {
5756

5857
private static final String LOG_TAG = "CronetChannelBuilder";
5958

inprocess/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
import io.grpc.ChannelCredentials;
2424
import io.grpc.ChannelLogger;
2525
import io.grpc.ExperimentalApi;
26+
import io.grpc.ForwardingChannelBuilder2;
2627
import io.grpc.Internal;
2728
import io.grpc.ManagedChannelBuilder;
28-
import io.grpc.internal.AbstractManagedChannelImplBuilder;
2929
import io.grpc.internal.ClientTransportFactory;
3030
import io.grpc.internal.ConnectionClientTransport;
3131
import io.grpc.internal.GrpcUtil;
@@ -47,7 +47,8 @@
4747
*/
4848
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1783")
4949
public final class InProcessChannelBuilder extends
50-
AbstractManagedChannelImplBuilder<InProcessChannelBuilder> {
50+
ForwardingChannelBuilder2<InProcessChannelBuilder> {
51+
5152
/**
5253
* Create a channel builder that will connect to the server with the given name.
5354
*

interop-testing/src/test/java/io/grpc/ChannelAndServerBuilderTest.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,11 @@ public static Collection<Object[]> params() throws Exception {
6666
Class<?> clazz = Class.forName(className, false /*initialize*/, loader);
6767
if (ServerBuilder.class.isAssignableFrom(clazz) && clazz != ServerBuilder.class) {
6868
classes.add(new Object[]{clazz});
69-
} else if (ManagedChannelBuilder.class.isAssignableFrom(clazz)
70-
&& clazz != ManagedChannelBuilder.class) {
69+
continue;
70+
}
71+
// ForwardingChannelBuilder extends ForwardingChannelBuilder2, not need for extra checks.
72+
if (ManagedChannelBuilder.class.isAssignableFrom(clazz)
73+
&& clazz != ManagedChannelBuilder.class && clazz != ForwardingChannelBuilder.class) {
7174
classes.add(new Object[]{clazz});
7275
}
7376
}

0 commit comments

Comments
 (0)