aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java b/src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java
new file mode 100644
index 0000000000..4eb1265fe1
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java
@@ -0,0 +1,173 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.unix;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+
+/**
+ * <p>An implementation of ServerSocket for local (AF_UNIX) sockets.
+ *
+ * <p>This class intentionally doesn't extend java.net.ServerSocket although it
+ * has some similarity to it. The java.net class hierarchy is a terrible mess
+ * and is inextricably coupled to the Internet Protocol.
+ *
+ * <p>This code is not intended to be portable to non-UNIX platforms.
+ */
+public class LocalServerSocket extends LocalSocket {
+
+ // Socket timeout in milliseconds. No timeout by default.
+ private long soTimeoutMillis = 0;
+
+ /**
+ * Constructs an unbound local server socket.
+ */
+ public LocalServerSocket() throws IOException {
+ super();
+ }
+
+ /**
+ * Constructs a server socket, binds it to the specified address, and
+ * listens for incoming connections with the specified backlog.
+ *
+ * @throws IOException if any of the socket/bind/listen operations failed.
+ */
+ public LocalServerSocket(LocalSocketAddress address, int backlog)
+ throws IOException {
+ this();
+ bind(address);
+ listen(backlog);
+ }
+
+ /**
+ * Constructs a server socket, binds it to the specified address, and begin
+ * listening for incoming connections using the default backlog.
+ *
+ * @throws IOException if any of the socket/bind/listen operations failed.
+ */
+ public LocalServerSocket(LocalSocketAddress address) throws IOException {
+ this(address, 50);
+ }
+
+ /**
+ * Specifies the timeout in milliseconds for accept(). Setting it to
+ * zero means an indefinite timeout.
+ */
+ public void setSoTimeout(long timeoutMillis) {
+ soTimeoutMillis = timeoutMillis;
+ }
+
+ /**
+ * Returns the current timeout in milliseconds.
+ */
+ public long getSoTimeout() {
+ return soTimeoutMillis;
+ }
+
+ /**
+ * Binds the specified address to this socket. The socket must be unbound.
+ * This causes the filesystem entry to appear.
+ *
+ * @throws IOException if the bind failed.
+ */
+ public synchronized void bind(LocalSocketAddress address)
+ throws IOException {
+ if (address == null) {
+ throw new NullPointerException("address");
+ }
+ checkNotClosed();
+ if (state != State.NEW) {
+ throw new SocketException("socket is already bound to an address");
+ }
+ bind(fd, address.getName().toString()); // JNI
+ this.address = address;
+ this.state = State.BOUND;
+ }
+
+ /**
+ * Listen for incoming connections on a socket using the specfied backlog.
+ * The socket must be bound but not already listening.
+ *
+ * @throws IOException if the listen failed.
+ */
+ public synchronized void listen(int backlog) throws IOException {
+ if (backlog < 1) {
+ throw new IllegalArgumentException("backlog=" + backlog);
+ }
+ checkNotClosed();
+ if (address == null) {
+ throw new SocketException("socket has no address bound");
+ }
+ if (state == State.LISTENING) {
+ throw new SocketException("socket is already listening");
+ }
+ listen(fd, backlog); // JNI
+ this.state = State.LISTENING;
+ }
+
+ /**
+ * Blocks until a connection is made to this socket and accepts it, returning
+ * a new socket connected to the client.
+ *
+ * @return the new socket connected to the client.
+ * @throws IOException if an error occurs when waiting for a connection.
+ * @throws SocketTimeoutException if a timeout was previously set with
+ * setSoTimeout and the timeout has been reached.
+ * @throws InterruptedIOException if the thread is interrupted when the
+ * method is blocked.
+ */
+ public synchronized Socket accept()
+ throws IOException, SocketTimeoutException, InterruptedIOException {
+ if (state != State.LISTENING) {
+ throw new SocketException("socket is not in listening state");
+ }
+
+ // Throws a SocketTimeoutException if timeout.
+ if (soTimeoutMillis != 0) {
+ poll(fd, soTimeoutMillis); // JNI
+ }
+
+ FileDescriptor clientFd = new FileDescriptor();
+ accept(fd, clientFd); // JNI
+ final LocalSocketImpl impl = new LocalSocketImpl(clientFd);
+ return new Socket(impl) {
+ @Override
+ public boolean isConnected() {
+ return true;
+ }
+ @Override
+ public synchronized void close() throws IOException {
+ if (isClosed()) {
+ return;
+ } else {
+ super.close();
+ // Workaround for the fact that super.created==false because we
+ // created the impl ourselves. As a result, super.close() doesn't
+ // call impl.close(). *Sigh*, java.net is horrendous.
+ // (Perhaps we should dispense with Socket/SocketImpl altogether?)
+ impl.close();
+ }
+ }
+ };
+ }
+
+ @Override
+ public String toString() {
+ return "LocalServerSocket(" + address + ")";
+ }
+}