• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20177

23 Feb 2026 03:46PM UTC coverage: 88.701% (-0.01%) from 88.714%
#20177

push

github

web-flow
Trigger R8's ServiceLoader optimization

This simplifies R8 Full Mode's configuration when paired with R8
optimizations (which is made more difficult to avoid in AGP 9), as when
the optimization is triggered Full Mode will automatically keep the
constructor for the relevant classes.

android-interop-test doesn't currently enable R8 optimizations, so it
doesn't actually demonstrate the benefit, but I have manually confirmed
that enabling proguard-android-optimize.txt does cause the ServiceLoader
optimization in android-interop-test. Note that full mode is a separate
configuration and not necessary to get the ServiceLoader optimization.

35461 of 39978 relevant lines covered (88.7%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.55
/../api/src/main/java/io/grpc/ServerRegistry.java
1
/*
2
 * Copyright 2020 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc;
18

19
import com.google.common.annotations.VisibleForTesting;
20
import com.google.common.base.Preconditions;
21
import com.google.errorprone.annotations.concurrent.GuardedBy;
22
import java.util.ArrayList;
23
import java.util.Collections;
24
import java.util.Comparator;
25
import java.util.LinkedHashSet;
26
import java.util.List;
27
import java.util.ServiceLoader;
28
import java.util.logging.Level;
29
import java.util.logging.Logger;
30
import javax.annotation.concurrent.ThreadSafe;
31

32
/**
33
 * Registry of {@link ServerProvider}s. The {@link #getDefaultRegistry default instance} loads
34
 * providers at runtime through the Java service provider mechanism.
35
 */
36
@Internal
37
@ThreadSafe
38
public final class ServerRegistry {
1✔
39
  private static final Logger logger = Logger.getLogger(ServerRegistry.class.getName());
1✔
40
  private static ServerRegistry instance;
41

42
  @GuardedBy("this")
1✔
43
  private final LinkedHashSet<ServerProvider> allProviders = new LinkedHashSet<>();
44
  /** Immutable, sorted version of {@code allProviders}. Is replaced instead of mutating. */
45
  @GuardedBy("this")
1✔
46
  private List<ServerProvider> effectiveProviders = Collections.emptyList();
1✔
47

48
  /**
49
   * Register a provider.
50
   *
51
   * <p>If the provider's {@link ServerProvider#isAvailable isAvailable()} returns
52
   * {@code false}, this method will throw {@link IllegalArgumentException}.
53
   *
54
   * <p>Providers will be used in priority order. In case of ties, providers are used in
55
   * registration order.
56
   */
57
  public synchronized void register(ServerProvider provider) {
58
    addProvider(provider);
1✔
59
    refreshProviders();
1✔
60
  }
1✔
61

62
  private synchronized void addProvider(ServerProvider provider) {
63
    Preconditions.checkArgument(provider.isAvailable(), "isAvailable() returned false");
1✔
64
    allProviders.add(provider);
1✔
65
  }
1✔
66

67
  /**
68
   * Deregisters a provider.  No-op if the provider is not in the registry.
69
   *
70
   * @param provider the provider that was added to the register via {@link #register}.
71
   */
72
  public synchronized void deregister(ServerProvider provider) {
73
    allProviders.remove(provider);
1✔
74
    refreshProviders();
1✔
75
  }
1✔
76

77
  private synchronized void refreshProviders() {
78
    List<ServerProvider> providers = new ArrayList<>(allProviders);
1✔
79
    // Sort descending based on priority.
80
    // sort() must be stable, as we prefer first-registered providers
81
    Collections.sort(providers, Collections.reverseOrder(new Comparator<ServerProvider>() {
1✔
82
      @Override
83
      public int compare(ServerProvider o1, ServerProvider o2) {
84
        return o1.priority() - o2.priority();
1✔
85
      }
86
    }));
87
    effectiveProviders = Collections.unmodifiableList(providers);
1✔
88
  }
1✔
89

90
  /**
91
   * Returns the default registry that loads providers via the Java service loader mechanism.
92
   */
93
  public static synchronized ServerRegistry getDefaultRegistry() {
94
    if (instance == null) {
1✔
95
      List<ServerProvider> providerList = ServiceProviders.loadAll(
1✔
96
          ServerProvider.class,
97
          ServiceLoader.load(ServerProvider.class, ServerProvider.class.getClassLoader())
1✔
98
            .iterator(),
1✔
99
          ServerRegistry::getHardCodedClasses,
100
          new ServerPriorityAccessor());
101
      instance = new ServerRegistry();
1✔
102
      for (ServerProvider provider : providerList) {
1✔
103
        logger.fine("Service loader found " + provider);
1✔
104
        instance.addProvider(provider);
1✔
105
      }
1✔
106
      instance.refreshProviders();
1✔
107
    }
108
    return instance;
1✔
109
  }
110

111
  /**
112
   * Returns effective providers, in priority order.
113
   */
114
  @VisibleForTesting
115
  synchronized List<ServerProvider> providers() {
116
    return effectiveProviders;
1✔
117
  }
118

119
  // For emulating ServerProvider.provider()
120
  ServerProvider provider() {
121
    List<ServerProvider> providers = providers();
1✔
122
    return providers.isEmpty() ? null : providers.get(0);
1✔
123
  }
124

125
  @VisibleForTesting
126
  static List<Class<?>> getHardCodedClasses() {
127
    // Class.forName(String) is used to remove the need for ProGuard configuration. Note that
128
    // ProGuard does not detect usages of Class.forName(String, boolean, ClassLoader):
129
    // https://sourceforge.net/p/proguard/bugs/418/
130
    List<Class<?>> list = new ArrayList<>();
1✔
131
    try {
132
      list.add(Class.forName("io.grpc.okhttp.OkHttpServerProvider"));
1✔
133
    } catch (ClassNotFoundException e) {
×
134
      logger.log(Level.FINE, "Unable to find OkHttpServerProvider", e);
×
135
    }
1✔
136
    return Collections.unmodifiableList(list);
1✔
137
  }
138

139
  ServerBuilder<?> newServerBuilderForPort(int port, ServerCredentials creds) {
140
    List<ServerProvider> providers = providers();
1✔
141
    if (providers.isEmpty()) {
1✔
142
      throw new ProviderNotFoundException("No functional server found. "
1✔
143
          + "Try adding a dependency on the grpc-netty, grpc-netty-shaded, or grpc-okhttp "
144
          + "artifact");
145
    }
146
    StringBuilder error = new StringBuilder();
1✔
147
    for (ServerProvider provider : providers()) {
1✔
148
      ServerProvider.NewServerBuilderResult result
1✔
149
          = provider.newServerBuilderForPort(port, creds);
1✔
150
      if (result.getServerBuilder() != null) {
1✔
151
        return result.getServerBuilder();
1✔
152
      }
153
      error.append("; ");
1✔
154
      error.append(provider.getClass().getName());
1✔
155
      error.append(": ");
1✔
156
      error.append(result.getError());
1✔
157
    }
1✔
158
    throw new ProviderNotFoundException(error.substring(2));
1✔
159
  }
160

161
  private static final class ServerPriorityAccessor
162
      implements ServiceProviders.PriorityAccessor<ServerProvider> {
163
    @Override
164
    public boolean isAvailable(ServerProvider provider) {
165
      return provider.isAvailable();
1✔
166
    }
167

168
    @Override
169
    public int getPriority(ServerProvider provider) {
170
      return provider.priority();
1✔
171
    }
172
  }
173

174
  /** Thrown when no suitable {@link ServerProvider} objects can be found. */
175
  public static final class ProviderNotFoundException extends RuntimeException {
176
    private static final long serialVersionUID = 1;
177

178
    public ProviderNotFoundException(String msg) {
179
      super(msg);
1✔
180
    }
1✔
181
  }
182
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc