1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix;
19
20 import java.io.Closeable;
21 import java.io.File;
22 import java.io.FileDescriptor;
23 import java.io.IOException;
24 import java.net.InetAddress;
25 import java.net.ServerSocket;
26 import java.net.SocketAddress;
27 import java.net.SocketException;
28 import java.net.SocketOption;
29 import java.net.SocketOptions;
30 import java.nio.channels.IllegalBlockingModeException;
31 import java.util.Objects;
32 import java.util.Set;
33 import java.util.concurrent.atomic.AtomicBoolean;
34
35 import org.eclipse.jdt.annotation.NonNull;
36 import org.eclipse.jdt.annotation.Nullable;
37
38 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
39
40
41
42
43
44
45
46 @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CouplingBetweenObjects"})
47 public abstract class AFServerSocket<A extends AFSocketAddress> extends ServerSocket implements
48 AFSomeSocketThing {
49 private final AFSocketImpl<A> implementation;
50 private @Nullable A boundEndpoint;
51 private final Closeables closeables = new Closeables();
52 private final AtomicBoolean created = new AtomicBoolean(false);
53 private final AtomicBoolean deleteOnClose = new AtomicBoolean(true);
54
55 @SuppressWarnings("this-escape")
56 private final AFServerSocketChannel<?> channel = newChannel();
57 private @Nullable SocketAddressFilter bindFilter;
58
59
60
61
62
63
64 public interface Constructor<A extends AFSocketAddress> {
65
66
67
68
69
70
71
72 @NonNull
73 AFServerSocket<A> newInstance(FileDescriptor fd) throws IOException;
74 }
75
76
77
78
79
80
81 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
82 protected AFServerSocket() throws IOException {
83 this(null);
84 }
85
86
87
88
89
90
91
92 @SuppressWarnings({"this-escape", "PMD.ConstructorCallsOverridableMethod"})
93 @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
94 protected AFServerSocket(FileDescriptor fdObj) throws IOException {
95 super();
96
97 this.implementation = newImpl(fdObj);
98 NativeUnixSocket.initServerImpl(this, implementation);
99
100 getAFImpl().setOption(SocketOptions.SO_REUSEADDR, true);
101 }
102
103
104
105
106
107
108 protected abstract AFServerSocketChannel<?> newChannel();
109
110
111
112
113
114
115
116
117 protected abstract AFSocketImpl<A> newImpl(FileDescriptor fdObj) throws IOException;
118
119
120
121
122
123
124
125
126
127 protected static <A extends AFSocketAddress> AFServerSocket<A> newInstance(
128 Constructor<A> instanceSupplier) throws IOException {
129 return instanceSupplier.newInstance(null);
130 }
131
132
133
134
135
136
137
138
139
140
141
142
143 protected static <A extends AFSocketAddress> AFServerSocket<A> newInstance(
144 Constructor<A> instanceSupplier, FileDescriptor fdObj, int localPort, int remotePort)
145 throws IOException {
146 if (fdObj == null) {
147 return instanceSupplier.newInstance(null);
148 }
149
150 int status = NativeUnixSocket.socketStatus(fdObj);
151 if (!fdObj.valid() || status == NativeUnixSocket.SOCKETSTATUS_INVALID) {
152 throw new SocketException("Not a valid socket");
153 }
154 AFServerSocket<A> socket = instanceSupplier.newInstance(fdObj);
155 socket.getAFImpl().updatePorts(localPort, remotePort);
156
157 switch (status) {
158 case NativeUnixSocket.SOCKETSTATUS_CONNECTED:
159 throw new SocketException("Not a ServerSocket");
160 case NativeUnixSocket.SOCKETSTATUS_BOUND:
161 socket.bind(AFSocketAddress.INTERNAL_DUMMY_BIND);
162
163 socket.setBoundEndpoint(AFSocketAddress.getSocketAddress(fdObj, false, localPort, socket
164 .addressFamily()));
165 break;
166 case NativeUnixSocket.SOCKETSTATUS_UNKNOWN:
167 break;
168 default:
169 throw new IllegalStateException("Invalid socketStatus response: " + status);
170 }
171
172 socket.getAFImpl().setSocketAddress(socket.getLocalSocketAddress());
173 return socket;
174 }
175
176
177
178
179
180
181
182
183
184
185 protected static <A extends AFSocketAddress> AFServerSocket<A> bindOn(
186 Constructor<A> instanceSupplier, final AFSocketAddress addr) throws IOException {
187 AFServerSocket<A> socket = instanceSupplier.newInstance(null);
188 socket.bind(addr);
189 return socket;
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203 protected static <A extends AFSocketAddress> AFServerSocket<A> bindOn(
204 Constructor<A> instanceSupplier, final A addr, boolean deleteOnClose) throws IOException {
205 AFServerSocket<A> socket = instanceSupplier.newInstance(null);
206 socket.bind(addr);
207 socket.setDeleteOnClose(deleteOnClose);
208 return socket;
209 }
210
211
212
213
214
215
216
217
218
219
220
221 protected static <A extends AFSocketAddress> AFServerSocket<A> forceBindOn(
222 Constructor<A> instanceSupplier, final A forceAddr) throws IOException {
223 AFServerSocket<A> socket = instanceSupplier.newInstance(null);
224 return socket.forceBindAddress(forceAddr);
225 }
226
227
228
229
230
231
232
233
234
235 public final AFServerSocket<A> forceBindAddress(SocketAddress endpoint) {
236 return bindHook((SocketAddress orig) -> {
237 return orig == null ? null : endpoint;
238 });
239 }
240
241 @SuppressWarnings("unchecked")
242 @Override
243 public final void bind(SocketAddress endpoint, int backlog) throws IOException {
244 if (isClosed()) {
245 throw new SocketException("Socket is closed");
246 }
247
248 boolean bindErrorOk;
249 if (bindFilter != null) {
250 endpoint = bindFilter.apply(endpoint);
251 bindErrorOk = endpoint != null && isBound();
252 } else {
253 bindErrorOk = false;
254 }
255
256 if (!(endpoint instanceof AFSocketAddress)) {
257 throw new IllegalArgumentException("Can only bind to endpoints of type "
258 + AFSocketAddress.class.getName() + ": " + endpoint);
259 }
260
261 A endpointCast;
262 try {
263 endpointCast = (A) endpoint;
264 } catch (ClassCastException e) {
265 throw new IllegalArgumentException("Can only bind to specific endpoints", e);
266 }
267
268 try {
269 getAFImpl().bind(endpoint, getReuseAddress() ? NativeUnixSocket.BIND_OPT_REUSE : 0);
270 } catch (SocketException e) {
271 if (bindErrorOk) {
272
273 return;
274 } else {
275 throw e;
276 }
277 }
278 setBoundEndpoint(getAFImpl().getLocalSocketAddress());
279 if (boundEndpoint0() == null) {
280 setBoundEndpoint(endpointCast);
281 }
282
283 if (endpoint == AFSocketAddress.INTERNAL_DUMMY_BIND) {
284 return;
285 }
286
287 implementation.listen(backlog);
288 }
289
290 @Override
291 public final boolean isBound() {
292 return boundEndpoint0() != null && implementation.getFD().valid();
293 }
294
295 @Override
296 public final boolean isClosed() {
297 return super.isClosed() || (isBound() && !implementation.getFD().valid()) || implementation
298 .isClosed();
299 }
300
301 @Override
302 public AFSocket<A> accept() throws IOException {
303 return accept1(true);
304 }
305
306 AFSocket<A> accept1(boolean throwOnFail) throws IOException {
307 AFSocket<A> as = newSocketInstance();
308
309 boolean success = implementation.accept0(as.getAFImpl(false));
310 if (isClosed()) {
311
312 throw new SocketClosedException("Socket is closed");
313 }
314
315 if (!success) {
316 if (throwOnFail) {
317 if (getChannel().isBlocking()) {
318
319 return null;
320 } else {
321
322 throw new IllegalBlockingModeException();
323 }
324 } else {
325 return null;
326 }
327 }
328
329 as.getAFImpl(true);
330 as.connect(AFSocketAddress.INTERNAL_DUMMY_CONNECT);
331 as.getAFImpl().updatePorts(getAFImpl().getLocalPort1(), getAFImpl().getRemotePort());
332
333 return as;
334 }
335
336
337
338
339
340
341
342 protected abstract AFSocket<A> newSocketInstance() throws IOException;
343
344 @Override
345 public String toString() {
346 return getClass().getSimpleName() + "[" + (isBound() ? boundEndpoint0() : "unbound") + "]";
347 }
348
349 @Override
350 public synchronized void close() throws IOException {
351 if (isClosed()) {
352 return;
353 }
354
355 boolean localSocketAddressValid = isLocalSocketAddressValid();
356
357 AFSocketAddress endpoint = boundEndpoint;
358
359 IOException superException = null;
360 try {
361 super.close();
362 } catch (IOException e) {
363 superException = e;
364 }
365 if (implementation != null) {
366 try {
367 implementation.close();
368 } catch (IOException e) {
369 if (superException == null) {
370 superException = e;
371 } else {
372 superException.addSuppressed(e);
373 }
374 }
375 }
376
377 IOException ex = null;
378 try {
379 closeables.close(superException);
380 } finally {
381 if (endpoint != null && endpoint.hasFilename() && localSocketAddressValid
382 && isDeleteOnClose()) {
383 File f = endpoint.getFile();
384 if (!f.delete() && f.exists()) {
385 ex = new IOException("Could not delete socket file after close: " + f);
386 }
387 }
388 }
389 if (ex != null) {
390 throw ex;
391 }
392 }
393
394
395
396
397
398
399 public final void addCloseable(Closeable closeable) {
400 closeables.add(closeable);
401 }
402
403
404
405
406
407
408 public final void removeCloseable(Closeable closeable) {
409 closeables.remove(closeable);
410 }
411
412
413
414
415
416
417 public static boolean isSupported() {
418 return NativeUnixSocket.isLoaded();
419 }
420
421 @Override
422 @SuppressFBWarnings("EI_EXPOSE_REP")
423 public final @Nullable A getLocalSocketAddress() {
424 @Nullable
425 A ep = boundEndpoint0();
426 if (ep == null) {
427 ep = getAFImpl().getLocalSocketAddress();
428 setBoundEndpoint(ep);
429 }
430 return ep;
431 }
432
433 private synchronized @Nullable A boundEndpoint0() {
434 return boundEndpoint;
435 }
436
437
438
439
440
441
442
443
444
445 public boolean isLocalSocketAddressValid() {
446 if (isClosed()) {
447 return false;
448 }
449 @Nullable
450 A addr = getLocalSocketAddress();
451 if (addr == null) {
452 return false;
453 }
454 return addr.equals(getAFImpl().getLocalSocketAddress());
455 }
456
457 final synchronized void setBoundEndpoint(@Nullable A addr) {
458 this.boundEndpoint = addr;
459 int port;
460 if (addr == null) {
461 port = -1;
462 } else {
463 port = addr.getPort();
464 }
465 getAFImpl().updatePorts(port, -1);
466 }
467
468 @Override
469 public final int getLocalPort() {
470 if (boundEndpoint0() == null) {
471 setBoundEndpoint(getAFImpl().getLocalSocketAddress());
472 }
473 if (boundEndpoint0() == null) {
474 return -1;
475 } else {
476 return getAFImpl().getLocalPort1();
477 }
478 }
479
480
481
482
483
484
485
486
487
488 public final boolean isDeleteOnClose() {
489 return deleteOnClose.get();
490 }
491
492
493
494
495
496
497
498
499
500
501 public final void setDeleteOnClose(boolean b) {
502 deleteOnClose.set(b);
503 }
504
505 final AFSocketImpl<A> getAFImpl() {
506 if (created.compareAndSet(false, true)) {
507 try {
508 getAFImpl().create(true);
509 getSoTimeout();
510 } catch (IOException e) {
511
512 }
513 }
514 return implementation;
515 }
516
517 @SuppressFBWarnings("EI_EXPOSE_REP")
518 @Override
519 public AFServerSocketChannel<?> getChannel() {
520 return channel;
521 }
522
523 @Override
524 public final FileDescriptor getFileDescriptor() throws IOException {
525 return implementation.getFileDescriptor();
526 }
527
528
529
530
531
532
533 protected final AFAddressFamily<A> addressFamily() {
534 return getAFImpl().getAddressFamily();
535 }
536
537
538
539
540
541
542
543
544
545
546 public final AFServerSocket<A> bindHook(SocketAddressFilter hook) {
547 this.bindFilter = hook;
548 return this;
549 }
550
551 @Override
552 public void bind(SocketAddress endpoint) throws IOException {
553 bind(endpoint, 50);
554 }
555
556 @Override
557 public InetAddress getInetAddress() {
558 if (!isBound()) {
559 return null;
560 } else {
561 return getAFImpl().getInetAddress();
562 }
563 }
564
565 @Override
566 public synchronized void setReceiveBufferSize(int size) throws SocketException {
567 if (size <= 0) {
568 throw new IllegalArgumentException("receive buffer size must be a positive number");
569 }
570 if (isClosed()) {
571 throw new SocketException("Socket is closed");
572 }
573 getAFImpl().setOption(SocketOptions.SO_RCVBUF, size);
574 }
575
576 @Override
577 public synchronized int getReceiveBufferSize() throws SocketException {
578 if (isClosed()) {
579 throw new SocketException("Socket is closed");
580 }
581 int result = 0;
582 Object o = getAFImpl().getOption(SocketOptions.SO_RCVBUF);
583 if (o instanceof Number) {
584 result = ((Number) o).intValue();
585 }
586 return result;
587 }
588
589 @Override
590 @SuppressWarnings("UnsynchronizedOverridesSynchronized" )
591 public void setSoTimeout(int timeout) throws SocketException {
592 if (isClosed()) {
593 throw new SocketException("Socket is closed");
594 }
595 if (timeout < 0) {
596 throw new IllegalArgumentException("timeout < 0");
597 }
598 getAFImpl().setOption(SocketOptions.SO_TIMEOUT, timeout);
599 }
600
601 @Override
602 @SuppressWarnings("UnsynchronizedOverridesSynchronized" )
603 public int getSoTimeout() throws IOException {
604 if (isClosed()) {
605 throw new SocketException("Socket is closed");
606 }
607 Object o = getAFImpl().getOption(SocketOptions.SO_TIMEOUT);
608
609 if (o instanceof Number) {
610 return ((Number) o).intValue();
611 } else {
612 return 0;
613 }
614 }
615
616 @Override
617 public void setReuseAddress(boolean on) throws SocketException {
618 if (isClosed()) {
619 throw new SocketException("Socket is closed");
620 }
621 getAFImpl().setOption(SocketOptions.SO_REUSEADDR, on);
622 }
623
624 @Override
625 public boolean getReuseAddress() throws SocketException {
626 if (isClosed()) {
627 throw new SocketException("Socket is closed");
628 }
629 return ((Boolean) (getAFImpl().getOption(SocketOptions.SO_REUSEADDR)));
630 }
631
632 @Override
633 public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
634 }
635
636 @SuppressWarnings({"all", "MissingOverride" })
637 public <T> T getOption(SocketOption<T> name) throws IOException {
638 Objects.requireNonNull(name);
639 if (isClosed()) {
640 throw new SocketException("Socket is closed");
641 }
642 return getAFImpl().getOption(name);
643 }
644
645 @SuppressWarnings({"all", "MissingOverride" })
646 public <T> ServerSocket setOption(SocketOption<T> name, T value) throws IOException {
647 Objects.requireNonNull(name);
648 if (isClosed()) {
649 throw new SocketException("Socket is closed");
650 }
651 getAFImpl().setOption(name, value);
652 return this;
653 }
654
655 @SuppressWarnings("all")
656 public Set<SocketOption<?>> supportedOptions() {
657 return getAFImpl().supportedOptions();
658 }
659
660 @Override
661 public void setShutdownOnClose(boolean enabled) {
662 getAFImpl().getCore().setShutdownOnClose(enabled);
663 }
664
665
666
667
668 }