From 51fd870cfd4b30f65477d51016a1781139f21c04 Mon Sep 17 00:00:00 2001 From: Carl Mastrangelo Date: Wed, 13 Apr 2016 10:48:37 -0700 Subject: [PATCH] Add getting the port out of a Server --- core/src/main/java/io/grpc/Server.java | 12 +++ .../io/grpc/inprocess/InProcessServer.java | 5 + .../main/java/io/grpc/internal/Server.java | 6 ++ .../java/io/grpc/internal/ServerImpl.java | 9 ++ .../grpc/inprocess/InProcessServerTest.java | 50 ++++++++++ .../java/io/grpc/internal/ServerImplTest.java | 48 ++++++++++ .../main/java/io/grpc/netty/NettyServer.java | 14 ++- .../java/io/grpc/netty/NettyServerTest.java | 96 +++++++++++++++++++ 8 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 core/src/test/java/io/grpc/inprocess/InProcessServerTest.java create mode 100644 netty/src/test/java/io/grpc/netty/NettyServerTest.java diff --git a/core/src/main/java/io/grpc/Server.java b/core/src/main/java/io/grpc/Server.java index 417feb9c08..504e6fa643 100644 --- a/core/src/main/java/io/grpc/Server.java +++ b/core/src/main/java/io/grpc/Server.java @@ -51,6 +51,18 @@ public abstract class Server { */ public abstract Server start() throws IOException; + /** + * Returns the port number the server is listening on. This can return -1 if there is no actual + * port or the result otherwise does not make sense. Result is undefined after the server is + * terminated. + * + * @throws IllegalStateException if the server has not yet been started. + */ + @ExperimentalApi + public int getPort() { + return -1; + } + /** * Initiates an orderly shutdown in which preexisting calls continue but new calls are rejected. */ diff --git a/core/src/main/java/io/grpc/inprocess/InProcessServer.java b/core/src/main/java/io/grpc/inprocess/InProcessServer.java index b90ddf516d..d706763aad 100644 --- a/core/src/main/java/io/grpc/inprocess/InProcessServer.java +++ b/core/src/main/java/io/grpc/inprocess/InProcessServer.java @@ -67,6 +67,11 @@ class InProcessServer implements Server { } } + @Override + public int getPort() { + return -1; + } + @Override public void shutdown() { if (!registry.remove(name, this)) { diff --git a/core/src/main/java/io/grpc/internal/Server.java b/core/src/main/java/io/grpc/internal/Server.java index 2cbe9e0dfd..14012e5ade 100644 --- a/core/src/main/java/io/grpc/internal/Server.java +++ b/core/src/main/java/io/grpc/internal/Server.java @@ -57,4 +57,10 @@ public interface Server { * method may only be called once. */ void shutdown(); + + /** + * Returns what underlying port the server is listening on, or -1 if the port number is not + * available or does not make sense. + */ + int getPort(); } diff --git a/core/src/main/java/io/grpc/internal/ServerImpl.java b/core/src/main/java/io/grpc/internal/ServerImpl.java index 171ad1c648..7e4944b4a5 100644 --- a/core/src/main/java/io/grpc/internal/ServerImpl.java +++ b/core/src/main/java/io/grpc/internal/ServerImpl.java @@ -139,6 +139,15 @@ public final class ServerImpl extends io.grpc.Server { } } + @Override + public int getPort() { + synchronized (lock) { + checkState(started, "Not started"); + checkState(!terminated, "Already terminated"); + return transportServer.getPort(); + } + } + /** * Initiates an orderly shutdown in which preexisting calls continue but new calls are rejected. */ diff --git a/core/src/test/java/io/grpc/inprocess/InProcessServerTest.java b/core/src/test/java/io/grpc/inprocess/InProcessServerTest.java new file mode 100644 index 0000000000..3c8aef93ff --- /dev/null +++ b/core/src/test/java/io/grpc/inprocess/InProcessServerTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.inprocess; + +import com.google.common.truth.Truth; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class InProcessServerTest { + + @Test + public void getPort_notStarted() throws Exception { + InProcessServer s = new InProcessServer("name"); + + Truth.assertThat(s.getPort()).isEqualTo(-1); + } +} + diff --git a/core/src/test/java/io/grpc/internal/ServerImplTest.java b/core/src/test/java/io/grpc/internal/ServerImplTest.java index b7ccee85b0..b66265f93b 100644 --- a/core/src/test/java/io/grpc/internal/ServerImplTest.java +++ b/core/src/test/java/io/grpc/internal/ServerImplTest.java @@ -47,6 +47,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import com.google.common.truth.Truth; import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Compressor; @@ -68,7 +69,9 @@ import io.grpc.StringMarshaller; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; @@ -99,6 +102,8 @@ public class ServerImplTest { private final DecompressorRegistry decompressorRegistry = DecompressorRegistry.getDefaultInstance(); + @Rule public final ExpectedException thrown = ExpectedException.none(); + @BeforeClass public static void beforeStartUp() { // Cancel the root context. Server will fork it so the per-call context should not @@ -511,6 +516,44 @@ public class ServerImplTest { assertTrue(latch.await(5, TimeUnit.SECONDS)); } + @Test + public void getPort() throws Exception { + transportServer = new SimpleServer() { + @Override + public int getPort() { + return 65535; + } + }; + ServerImpl server = new ServerImpl(executor, registry, transportServer, SERVER_CONTEXT, + decompressorRegistry, compressorRegistry); + server.start(); + + Truth.assertThat(server.getPort()).isEqualTo(65535); + } + + @Test + public void getPortBeforeStartedFails() { + transportServer = new SimpleServer(); + ServerImpl server = new ServerImpl(executor, registry, transportServer, SERVER_CONTEXT, + decompressorRegistry, compressorRegistry); + thrown.expect(IllegalStateException.class); + thrown.expectMessage("started"); + server.getPort(); + } + + @Test + public void getPortAfterTerminationFails() throws Exception { + transportServer = new SimpleServer(); + ServerImpl server = new ServerImpl(executor, registry, transportServer, SERVER_CONTEXT, + decompressorRegistry, compressorRegistry); + server.start(); + server.shutdown(); + server.awaitTermination(); + thrown.expect(IllegalStateException.class); + thrown.expectMessage("terminated"); + server.getPort(); + } + /** * Useful for plugging a single-threaded executor from processing tasks, or for waiting until a * single-threaded executor has processed queued tasks. @@ -541,6 +584,11 @@ public class ServerImplTest { this.listener = listener; } + @Override + public int getPort() { + return -1; + } + @Override public void shutdown() { listener.serverShutdown(); diff --git a/netty/src/main/java/io/grpc/netty/NettyServer.java b/netty/src/main/java/io/grpc/netty/NettyServer.java index 0ce9cf83eb..b0878991b3 100644 --- a/netty/src/main/java/io/grpc/netty/NettyServer.java +++ b/netty/src/main/java/io/grpc/netty/NettyServer.java @@ -50,6 +50,7 @@ import io.netty.util.AbstractReferenceCounted; import io.netty.util.ReferenceCounted; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.logging.Level; import java.util.logging.Logger; @@ -94,6 +95,18 @@ class NettyServer implements Server { this.maxHeaderListSize = maxHeaderListSize; } + @Override + public int getPort() { + if (channel == null) { + return -1; + } + SocketAddress localAddr = channel.localAddress(); + if (!(localAddr instanceof InetSocketAddress)) { + return -1; + } + return ((InetSocketAddress)localAddr).getPort(); + } + @Override public void start(ServerListener serverListener) throws IOException { listener = checkNotNull(serverListener, "serverListener"); @@ -123,7 +136,6 @@ class NettyServer implements Server { transport.start(listener.transportCreated(transport)); } }); - // Bind and start to accept incoming connections. ChannelFuture future = b.bind(address); try { diff --git a/netty/src/test/java/io/grpc/netty/NettyServerTest.java b/netty/src/test/java/io/grpc/netty/NettyServerTest.java new file mode 100644 index 0000000000..ffc5c64e64 --- /dev/null +++ b/netty/src/test/java/io/grpc/netty/NettyServerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2016, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.netty; + +import static com.google.common.truth.Truth.assertThat; + +import io.grpc.internal.ServerListener; +import io.grpc.internal.ServerTransport; +import io.grpc.internal.ServerTransportListener; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.InetSocketAddress; + +@RunWith(JUnit4.class) +public class NettyServerTest { + + @Test + public void getPort() throws Exception { + InetSocketAddress addr = new InetSocketAddress(0); + NettyServer ns = new NettyServer( + addr, + NioServerSocketChannel.class, + null, // no boss group + null, // no event group + new ProtocolNegotiators.PlaintextNegotiator(), + 1, // ignore + 1, // ignore + 1, // ignore + 1); // ignore + ns.start(new ServerListener() { + @Override + public ServerTransportListener transportCreated(ServerTransport transport) { + return null; + } + + @Override + public void serverShutdown() {} + }); + + // Check that we got an actual port. + assertThat(ns.getPort()).isGreaterThan(0); + + // Cleanup + ns.shutdown(); + } + + @Test + public void getPort_notStarted() throws Exception { + InetSocketAddress addr = new InetSocketAddress(0); + NettyServer ns = new NettyServer( + addr, + NioServerSocketChannel.class, + null, // no boss group + null, // no event group + new ProtocolNegotiators.PlaintextNegotiator(), + 1, // ignore + 1, // ignore + 1, // ignore + 1); // ignore + + assertThat(ns.getPort()).isEqualTo(-1); + } +}