View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2024 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix;
19  
20  import static java.util.Objects.requireNonNull;
21  
22  import java.io.FileDescriptor;
23  import java.io.IOException;
24  import java.net.ProtocolFamily;
25  import java.net.ServerSocket;
26  import java.net.SocketAddress;
27  import java.net.SocketOption;
28  import java.net.StandardProtocolFamily;
29  import java.nio.channels.ServerSocketChannel;
30  import java.nio.channels.spi.SelectorProvider;
31  import java.util.Objects;
32  import java.util.Set;
33  
34  import org.eclipse.jdt.annotation.NonNull;
35  import org.eclipse.jdt.annotation.Nullable;
36  
37  import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
38  
39  /**
40   * A selectable channel for stream-oriented listening sockets.
41   *
42   * @param <A> The concrete {@link AFSocketAddress} that is supported by this type.
43   * @author Christian Kohlschütter
44   */
45  public abstract class AFServerSocketChannel<A extends AFSocketAddress> extends ServerSocketChannel
46      implements FileDescriptorAccess, AFSomeSocketChannel {
47    private final @NonNull AFServerSocket<A> afSocket;
48  
49    /**
50     * Creates a new {@link AFServerSocketChannel} instance.
51     *
52     * @param socket The corresponding {@link ServerSocket}.
53     * @param sp The corresponding {@link SelectorProvider}.
54     */
55    @SuppressWarnings("all") // null
56    protected AFServerSocketChannel(AFServerSocket<A> socket, AFSelectorProvider<A> sp) {
57      super(sp);
58      this.afSocket = Objects.requireNonNull(socket);
59    }
60  
61    // CPD-OFF
62  
63    @SuppressWarnings("unchecked")
64    @Override
65    public <T> T getOption(SocketOption<T> name) throws IOException {
66      if (name instanceof AFSocketOption<?>) {
67        return getAFCore().getOption((AFSocketOption<T>) name);
68      }
69      Integer optionId = SocketOptionsMapper.resolve(name);
70      if (optionId == null) {
71        throw new UnsupportedOperationException("unsupported option");
72      } else {
73        return (T) afSocket.getAFImpl().getOption(optionId);
74      }
75    }
76  
77    @Override
78    public <T> AFServerSocketChannel<A> setOption(SocketOption<T> name, T value) throws IOException {
79      if (name instanceof AFSocketOption<?>) {
80        getAFCore().setOption((AFSocketOption<T>) name, value);
81        return this;
82      }
83      Integer optionId = SocketOptionsMapper.resolve(name);
84      if (optionId == null) {
85        throw new UnsupportedOperationException("unsupported option");
86      } else {
87        afSocket.getAFImpl().setOption(optionId, value);
88      }
89      return this;
90    }
91  
92    @Override
93    public final Set<SocketOption<?>> supportedOptions() {
94      return SocketOptionsMapper.SUPPORTED_SOCKET_OPTIONS;
95    }
96  
97    // CPD-ON
98  
99    @Override
100   public final AFServerSocketChannel<A> bind(SocketAddress local, int backlog) throws IOException {
101     afSocket.bind(local, backlog);
102     return this;
103   }
104 
105   @SuppressFBWarnings("EI_EXPOSE_REP")
106   @Override
107   public final AFServerSocket<A> socket() {
108     return afSocket;
109   }
110 
111   @Override
112   public AFSocketChannel<A> accept() throws IOException {
113     boolean complete = false;
114     Exception exception = null;
115     try {
116       begin();
117       AFSocket<A> socket = afSocket.accept1(false);
118       complete = true;
119       return socket == null ? null : socket.getChannel();
120     } catch (IOException e) {
121       throw InterruptibleChannelUtil.ioExceptionOrThrowRuntimeException( // NOPMD.PreserveStackTrace
122           (exception = InterruptibleChannelUtil.handleException(this, e)));
123     } finally {
124       InterruptibleChannelUtil.endInterruptable(this, this::end, complete, exception);
125     }
126   }
127 
128   @Override
129   public final @Nullable A getLocalAddress() {
130     return getLocalSocketAddress();
131   }
132 
133   @Override
134   public final @Nullable A getLocalSocketAddress() {
135     return afSocket.getLocalSocketAddress();
136   }
137 
138   /**
139    * Checks if the local socket address returned by {@link #getLocalAddress()} is still valid.
140    *
141    * The address is no longer valid if the server socket has been closed, {@code null}, or another
142    * server socket has been bound on that address.
143    *
144    * @return {@code true} iff still valid.
145    */
146   public final boolean isLocalSocketAddressValid() {
147     return afSocket.isLocalSocketAddressValid();
148   }
149 
150   @Override
151   protected final void implCloseSelectableChannel() throws IOException {
152     afSocket.close();
153   }
154 
155   @Override
156   protected final void implConfigureBlocking(boolean block) throws IOException {
157     getAFCore().implConfigureBlocking(block);
158   }
159 
160   final AFSocketCore getAFCore() {
161     return afSocket.getAFImpl().getCore();
162   }
163 
164   @Override
165   public final FileDescriptor getFileDescriptor() throws IOException {
166     return afSocket.getFileDescriptor();
167   }
168 
169   /**
170    * Checks if this {@link AFServerSocketChannel}'s file should be removed upon {@link #close()}.
171    *
172    * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract
173    * namespace).
174    *
175    * @return {@code true} if an attempt is made to delete the socket file upon {@link #close()}.
176    */
177   public final boolean isDeleteOnClose() {
178     return socket().isDeleteOnClose();
179   }
180 
181   /**
182    * Enables/disables deleting this {@link AFServerSocketChannel}'s file (or other resource type)
183    * upon {@link #close()}.
184    *
185    * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract
186    * namespace).
187    *
188    * @param b Enabled if {@code true}.
189    */
190   public final void setDeleteOnClose(boolean b) {
191     socket().setDeleteOnClose(b);
192   }
193 
194   @Override
195   public void setShutdownOnClose(boolean enabled) {
196     socket().setShutdownOnClose(enabled);
197   }
198 
199   /**
200    * Opens a server-socket channel. The {@code family} parameter specifies the {@link ProtocolFamily
201    * protocol family} of the channel's socket.
202    * <p>
203    * If the {@link ProtocolFamily} is of an {@link AFProtocolFamily}, or {@code UNIX}, the
204    * corresponding junixsocket implementation is used. In all other cases, the call is delegated to
205    * {@link ServerSocketChannel#open()}.
206    *
207    * @param family The protocol family.
208    * @return The new {@link ServerSocketChannel}.
209    * @throws IOException on error.
210    */
211   public static ServerSocketChannel open(ProtocolFamily family) throws IOException {
212     requireNonNull(family);
213 
214     if (family instanceof AFProtocolFamily) {
215       return ((AFProtocolFamily) family).openServerSocketChannel();
216     } else if ("UNIX".equals(family.name())) {
217       return AFUNIXServerSocketChannel.open();
218     } else if (family instanceof StandardProtocolFamily) {
219       return ServerSocketChannel.open();
220     } else {
221       throw new UnsupportedOperationException("Protocol family not supported");
222     }
223   }
224 }