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

grpc / grpc-java / #20230

31 Mar 2026 09:55AM UTC coverage: 88.734% (+0.01%) from 88.72%
#20230

push

github

web-flow
openTelemetry: add tcp metrics (#12652)

Implements [A80](https://github.com/grpc/proposal/pull/519)

35697 of 40229 relevant lines covered (88.73%)

0.89 hits per line

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

84.13
/../servlet/src/main/java/io/grpc/servlet/ServletServerBuilder.java
1
/*
2
 * Copyright 2018 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.servlet;
18

19
import static com.google.common.base.Preconditions.checkArgument;
20
import static com.google.common.base.Preconditions.checkNotNull;
21
import static com.google.common.base.Preconditions.checkState;
22
import static io.grpc.internal.GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
23

24
import com.google.common.annotations.VisibleForTesting;
25
import com.google.common.util.concurrent.ListenableFuture;
26
import io.grpc.Attributes;
27
import io.grpc.ExperimentalApi;
28
import io.grpc.ForwardingServerBuilder;
29
import io.grpc.Internal;
30
import io.grpc.InternalChannelz.SocketStats;
31
import io.grpc.InternalInstrumented;
32
import io.grpc.InternalLogId;
33
import io.grpc.Metadata;
34
import io.grpc.Server;
35
import io.grpc.ServerBuilder;
36
import io.grpc.ServerStreamTracer;
37
import io.grpc.Status;
38
import io.grpc.internal.GrpcUtil;
39
import io.grpc.internal.InternalServer;
40
import io.grpc.internal.ServerImplBuilder;
41
import io.grpc.internal.ServerListener;
42
import io.grpc.internal.ServerStream;
43
import io.grpc.internal.ServerTransport;
44
import io.grpc.internal.ServerTransportListener;
45
import io.grpc.internal.SharedResourceHolder;
46
import java.io.File;
47
import java.io.IOException;
48
import java.net.SocketAddress;
49
import java.util.Collections;
50
import java.util.List;
51
import java.util.concurrent.ScheduledExecutorService;
52
import java.util.function.Function;
53
import javax.annotation.Nullable;
54
import javax.annotation.concurrent.NotThreadSafe;
55
import javax.servlet.http.HttpServletRequest;
56

57
/**
58
 * Builder to build a gRPC server that can run as a servlet. This is for advanced custom settings.
59
 * Normally, users should consider extending the out-of-box {@link GrpcServlet} directly instead.
60
 *
61
 * <p>The API is experimental. The authors would like to know more about the real usecases. Users
62
 * are welcome to provide feedback by commenting on
63
 * <a href=https://github.com/grpc/grpc-java/issues/5066>the tracking issue</a>.
64
 */
65
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/5066")
66
@NotThreadSafe
67
public final class ServletServerBuilder extends ForwardingServerBuilder<ServletServerBuilder> {
68
  List<? extends ServerStreamTracer.Factory> streamTracerFactories;
69
  private Function<HttpServletRequest, String> methodNameResolver =
1✔
70
      ServletAdapter.DEFAULT_METHOD_NAME_RESOLVER;
71
  int maxInboundMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
1✔
72

73
  private final ServerImplBuilder serverImplBuilder;
74

75
  private ScheduledExecutorService scheduler;
76
  private boolean internalCaller;
77
  private boolean usingCustomScheduler;
78
  private InternalServerImpl internalServer;
79

80
  public ServletServerBuilder() {
1✔
81
    serverImplBuilder = new ServerImplBuilder(
1✔
82
        (streamTracerFactories, metricRecorder) -> 
83
            buildTransportServers(streamTracerFactories));
1✔
84
  }
1✔
85

86
  /**
87
   * Builds a gRPC server that can run as a servlet.
88
   *
89
   * <p>The returned server will not be started or bound to a port.
90
   *
91
   * <p>Users should not call this method directly. Instead users should call
92
   * {@link #buildServletAdapter()} which internally will call {@code build()} and {@code start()}
93
   * appropriately.
94
   *
95
   * @throws IllegalStateException if this method is called by users directly
96
   */
97
  @Override
98
  public Server build() {
99
    checkState(internalCaller, "build() method should not be called directly by an application");
1✔
100
    return super.build();
1✔
101
  }
102

103
  /**
104
   * Creates a {@link ServletAdapter}.
105
   */
106
  public ServletAdapter buildServletAdapter() {
107
    return new ServletAdapter(buildAndStart(), streamTracerFactories, methodNameResolver,
1✔
108
        maxInboundMessageSize);
109
  }
110

111
  /**
112
   * Creates a {@link GrpcServlet}.
113
   */
114
  public GrpcServlet buildServlet() {
115
    return new GrpcServlet(buildServletAdapter());
×
116
  }
117

118
  private ServerTransportListener buildAndStart() {
119
    Server server;
120
    try {
121
      internalCaller = true;
1✔
122
      server = build().start();
1✔
123
    } catch (IOException e) {
×
124
      // actually this should never happen
125
      throw new RuntimeException(e);
×
126
    } finally {
127
      internalCaller = false;
1✔
128
    }
129

130
    if (!usingCustomScheduler) {
1✔
131
      scheduler = SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE);
1✔
132
    }
133

134
    // Create only one "transport" for all requests because it has no knowledge of which request is
135
    // associated with which client socket. This "transport" does not do socket connection, the
136
    // container does.
137
    ServerTransportImpl serverTransport = new ServerTransportImpl(scheduler);
1✔
138
    ServerTransportListener delegate =
1✔
139
        internalServer.serverListener.transportCreated(serverTransport);
1✔
140
    return new ServerTransportListener() {
1✔
141
      @Override
142
      public void streamCreated(ServerStream stream, String method, Metadata headers) {
143
        delegate.streamCreated(stream, method, headers);
1✔
144
      }
1✔
145

146
      @Override
147
      public Attributes transportReady(Attributes attributes) {
148
        return delegate.transportReady(attributes);
1✔
149
      }
150

151
      @Override
152
      public void transportTerminated() {
153
        server.shutdown();
1✔
154
        delegate.transportTerminated();
1✔
155
        if (!usingCustomScheduler) {
1✔
156
          SharedResourceHolder.release(GrpcUtil.TIMER_SERVICE, scheduler);
1✔
157
        }
158
      }
1✔
159
    };
160
  }
161

162
  @VisibleForTesting
163
  InternalServer buildTransportServers(
164
      List<? extends ServerStreamTracer.Factory> streamTracerFactories) {
165
    checkNotNull(streamTracerFactories, "streamTracerFactories");
1✔
166
    this.streamTracerFactories = streamTracerFactories;
1✔
167
    internalServer = new InternalServerImpl();
1✔
168
    return internalServer;
1✔
169
  }
170

171
  @Internal
172
  @Override
173
  protected ServerBuilder<?> delegate() {
174
    return serverImplBuilder;
1✔
175
  }
176

177
  /**
178
   * Throws {@code UnsupportedOperationException}. TLS should be configured by the servlet
179
   * container.
180
   */
181
  @Override
182
  public ServletServerBuilder useTransportSecurity(File certChain, File privateKey) {
183
    throw new UnsupportedOperationException("TLS should be configured by the servlet container");
×
184
  }
185

186
  /**
187
   * Specifies how to determine gRPC method name from servlet request.
188
   *
189
   * <p>The default strategy is using {@link HttpServletRequest#getRequestURI()} without the leading
190
   * slash.</p>
191
   */
192
  public ServletServerBuilder methodNameResolver(
193
      Function<HttpServletRequest, String> methodResolver) {
194
    this.methodNameResolver = checkNotNull(methodResolver);
×
195
    return this;
×
196
  }
197

198
  @Override
199
  public ServletServerBuilder maxInboundMessageSize(int bytes) {
200
    checkArgument(bytes >= 0, "bytes must be >= 0");
1✔
201
    maxInboundMessageSize = bytes;
1✔
202
    return this;
1✔
203
  }
204

205
  /**
206
   * Provides a custom scheduled executor service to the server builder.
207
   *
208
   * @return this
209
   */
210
  public ServletServerBuilder scheduledExecutorService(ScheduledExecutorService scheduler) {
211
    this.scheduler = checkNotNull(scheduler, "scheduler");
1✔
212
    usingCustomScheduler = true;
1✔
213
    return this;
1✔
214
  }
215

216
  private static final class InternalServerImpl implements InternalServer {
217

218
    ServerListener serverListener;
219

220
    InternalServerImpl() {}
1✔
221

222
    @Override
223
    public void start(ServerListener listener) {
224
      serverListener = listener;
1✔
225
    }
1✔
226

227
    @Override
228
    public void shutdown() {
229
      if (serverListener != null) {
1✔
230
        serverListener.serverShutdown();
1✔
231
      }
232
    }
1✔
233

234
    @Override
235
    public SocketAddress getListenSocketAddress() {
236
      return new SocketAddress() {
1✔
237
        @Override
238
        public String toString() {
239
          return "ServletServer";
×
240
        }
241
      };
242
    }
243

244
    @Override
245
    public InternalInstrumented<SocketStats> getListenSocketStats() {
246
      // sockets are managed by the servlet container, grpc is ignorant of that
247
      return null;
×
248
    }
249

250
    @Override
251
    public List<? extends SocketAddress> getListenSocketAddresses() {
252
      return Collections.emptyList();
1✔
253
    }
254

255
    @Nullable
256
    @Override
257
    public List<InternalInstrumented<SocketStats>> getListenSocketStatsList() {
258
      return null;
×
259
    }
260
  }
261

262
  @VisibleForTesting
263
  static final class ServerTransportImpl implements ServerTransport {
264

265
    private final InternalLogId logId = InternalLogId.allocate(ServerTransportImpl.class, null);
1✔
266
    private final ScheduledExecutorService scheduler;
267

268
    ServerTransportImpl(ScheduledExecutorService scheduler) {
1✔
269
      this.scheduler = checkNotNull(scheduler, "scheduler");
1✔
270
    }
1✔
271

272
    @Override
273
    public void shutdown() {}
1✔
274

275
    @Override
276
    public void shutdownNow(Status reason) {}
1✔
277

278
    @Override
279
    public ScheduledExecutorService getScheduledExecutorService() {
280
      return scheduler;
1✔
281
    }
282

283
    @Override
284
    public ListenableFuture<SocketStats> getStats() {
285
      // does not support instrumentation
286
      return null;
×
287
    }
288

289
    @Override
290
    public InternalLogId getLogId() {
291
      return logId;
1✔
292
    }
293
  }
294
}
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