1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel;
17
18 import io.netty.channel.socket.ChannelOutputShutdownEvent;
19 import io.netty.channel.socket.ChannelOutputShutdownException;
20 import io.netty.util.DefaultAttributeMap;
21 import io.netty.util.ReferenceCountUtil;
22 import io.netty.util.internal.ObjectUtil;
23 import io.netty.util.internal.PlatformDependent;
24 import io.netty.util.internal.logging.InternalLogger;
25 import io.netty.util.internal.logging.InternalLoggerFactory;
26
27 import java.io.IOException;
28 import java.net.ConnectException;
29 import java.net.InetSocketAddress;
30 import java.net.NoRouteToHostException;
31 import java.net.SocketAddress;
32 import java.net.SocketException;
33 import java.nio.channels.ClosedChannelException;
34 import java.nio.channels.NotYetConnectedException;
35 import java.util.concurrent.Executor;
36 import java.util.concurrent.RejectedExecutionException;
37
38
39
40
41 public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
42
43 private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractChannel.class);
44
45 private final Channel parent;
46 private final ChannelId id;
47 private final Unsafe unsafe;
48 private final DefaultChannelPipeline pipeline;
49 private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);
50 private final CloseFuture closeFuture = new CloseFuture(this);
51
52 private volatile SocketAddress localAddress;
53 private volatile SocketAddress remoteAddress;
54 private volatile EventLoop eventLoop;
55 private volatile boolean registered;
56 private boolean closeInitiated;
57 private Throwable initialCloseCause;
58
59
60 private boolean strValActive;
61 private String strVal;
62
63
64
65
66
67
68
69 protected AbstractChannel(Channel parent) {
70 this.parent = parent;
71 id = newId();
72 unsafe = newUnsafe();
73 pipeline = newChannelPipeline();
74 }
75
76
77
78
79
80
81
82 protected AbstractChannel(Channel parent, ChannelId id) {
83 this.parent = parent;
84 this.id = id;
85 unsafe = newUnsafe();
86 pipeline = newChannelPipeline();
87 }
88
89 protected final int maxMessagesPerWrite() {
90 ChannelConfig config = config();
91 if (config instanceof DefaultChannelConfig) {
92 return ((DefaultChannelConfig) config).getMaxMessagesPerWrite();
93 }
94 Integer value = config.getOption(ChannelOption.MAX_MESSAGES_PER_WRITE);
95 if (value == null) {
96 return Integer.MAX_VALUE;
97 }
98 return value;
99 }
100
101 @Override
102 public final ChannelId id() {
103 return id;
104 }
105
106
107
108
109
110 protected ChannelId newId() {
111 return DefaultChannelId.newInstance();
112 }
113
114
115
116
117 protected DefaultChannelPipeline newChannelPipeline() {
118 return new DefaultChannelPipeline(this);
119 }
120
121 @Override
122 public Channel parent() {
123 return parent;
124 }
125
126 @Override
127 public ChannelPipeline pipeline() {
128 return pipeline;
129 }
130
131 @Override
132 public EventLoop eventLoop() {
133 EventLoop eventLoop = this.eventLoop;
134 if (eventLoop == null) {
135 throw new IllegalStateException("channel not registered to an event loop");
136 }
137 return eventLoop;
138 }
139
140 @Override
141 public SocketAddress localAddress() {
142 SocketAddress localAddress = this.localAddress;
143 if (localAddress == null) {
144 try {
145 this.localAddress = localAddress = unsafe().localAddress();
146 } catch (Error e) {
147 throw e;
148 } catch (Throwable t) {
149
150 return null;
151 }
152 }
153 return localAddress;
154 }
155
156
157
158
159 @Deprecated
160 protected void invalidateLocalAddress() {
161 localAddress = null;
162 }
163
164 @Override
165 public SocketAddress remoteAddress() {
166 SocketAddress remoteAddress = this.remoteAddress;
167 if (remoteAddress == null) {
168 try {
169 this.remoteAddress = remoteAddress = unsafe().remoteAddress();
170 } catch (Error e) {
171 throw e;
172 } catch (Throwable t) {
173
174 return null;
175 }
176 }
177 return remoteAddress;
178 }
179
180
181
182
183 @Deprecated
184 protected void invalidateRemoteAddress() {
185 remoteAddress = null;
186 }
187
188 @Override
189 public boolean isRegistered() {
190 return registered;
191 }
192
193 @Override
194 public ChannelFuture closeFuture() {
195 return closeFuture;
196 }
197
198 @Override
199 public Unsafe unsafe() {
200 return unsafe;
201 }
202
203
204
205
206 protected abstract AbstractUnsafe newUnsafe();
207
208
209
210
211 @Override
212 public final int hashCode() {
213 return id.hashCode();
214 }
215
216
217
218
219
220 @Override
221 public final boolean equals(Object o) {
222 return this == o;
223 }
224
225 @Override
226 public final int compareTo(Channel o) {
227 if (this == o) {
228 return 0;
229 }
230
231 return id().compareTo(o.id());
232 }
233
234
235
236
237
238
239
240 @Override
241 public String toString() {
242 boolean active = isActive();
243 if (strValActive == active && strVal != null) {
244 return strVal;
245 }
246
247 SocketAddress remoteAddr = remoteAddress();
248 SocketAddress localAddr = localAddress();
249 if (remoteAddr != null) {
250 StringBuilder buf = new StringBuilder(96)
251 .append("[id: 0x")
252 .append(id.asShortText())
253 .append(", L:")
254 .append(localAddr)
255 .append(active? " - " : " ! ")
256 .append("R:")
257 .append(remoteAddr)
258 .append(']');
259 strVal = buf.toString();
260 } else if (localAddr != null) {
261 StringBuilder buf = new StringBuilder(64)
262 .append("[id: 0x")
263 .append(id.asShortText())
264 .append(", L:")
265 .append(localAddr)
266 .append(']');
267 strVal = buf.toString();
268 } else {
269 StringBuilder buf = new StringBuilder(16)
270 .append("[id: 0x")
271 .append(id.asShortText())
272 .append(']');
273 strVal = buf.toString();
274 }
275
276 strValActive = active;
277 return strVal;
278 }
279
280 @Override
281 public final ChannelPromise voidPromise() {
282 return pipeline.voidPromise();
283 }
284
285
286
287
288 protected abstract class AbstractUnsafe implements Unsafe {
289
290 private volatile ChannelOutboundBuffer outboundBuffer = new ChannelOutboundBuffer(AbstractChannel.this);
291 private RecvByteBufAllocator.Handle recvHandle;
292 private boolean inFlush0;
293
294 private boolean neverRegistered = true;
295
296 private void assertEventLoop() {
297 assert !registered || eventLoop.inEventLoop();
298 }
299
300 @Override
301 public RecvByteBufAllocator.Handle recvBufAllocHandle() {
302 if (recvHandle == null) {
303 recvHandle = config().getRecvByteBufAllocator().newHandle();
304 }
305 return recvHandle;
306 }
307
308 @Override
309 public final ChannelOutboundBuffer outboundBuffer() {
310 return outboundBuffer;
311 }
312
313 @Override
314 public final SocketAddress localAddress() {
315 return localAddress0();
316 }
317
318 @Override
319 public final SocketAddress remoteAddress() {
320 return remoteAddress0();
321 }
322
323 @Override
324 public final void register(EventLoop eventLoop, final ChannelPromise promise) {
325 ObjectUtil.checkNotNull(eventLoop, "eventLoop");
326 if (isRegistered()) {
327 promise.setFailure(new IllegalStateException("registered to an event loop already"));
328 return;
329 }
330 if (!isCompatible(eventLoop)) {
331 promise.setFailure(
332 new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
333 return;
334 }
335
336 AbstractChannel.this.eventLoop = eventLoop;
337
338 if (eventLoop.inEventLoop()) {
339 register0(promise);
340 } else {
341 try {
342 eventLoop.execute(new Runnable() {
343 @Override
344 public void run() {
345 register0(promise);
346 }
347 });
348 } catch (Throwable t) {
349 logger.warn(
350 "Force-closing a channel whose registration task was not accepted by an event loop: {}",
351 AbstractChannel.this, t);
352 closeForcibly();
353 closeFuture.setClosed();
354 safeSetFailure(promise, t);
355 }
356 }
357 }
358
359 private void register0(ChannelPromise promise) {
360
361
362 if (!promise.setUncancellable() || !ensureOpen(promise)) {
363 return;
364 }
365 ChannelPromise registerPromise = newPromise();
366 boolean firstRegistration = neverRegistered;
367 registerPromise.addListener(new ChannelFutureListener() {
368 @Override
369 public void operationComplete(ChannelFuture future) throws Exception {
370 if (future.isSuccess()) {
371 neverRegistered = false;
372 registered = true;
373
374
375
376 pipeline.invokeHandlerAddedIfNeeded();
377
378 safeSetSuccess(promise);
379 pipeline.fireChannelRegistered();
380
381
382 if (isActive()) {
383 if (firstRegistration) {
384 pipeline.fireChannelActive();
385 } else if (config().isAutoRead()) {
386
387
388
389
390 beginRead();
391 }
392 }
393 } else {
394
395 closeForcibly();
396 closeFuture.setClosed();
397 safeSetFailure(promise, future.cause());
398 }
399 }
400 });
401 doRegister(registerPromise);
402 }
403
404 @Override
405 public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
406 assertEventLoop();
407
408 if (!promise.setUncancellable() || !ensureOpen(promise)) {
409 return;
410 }
411
412
413 if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
414 localAddress instanceof InetSocketAddress &&
415 !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
416 !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
417
418
419 logger.warn(
420 "A non-root user can't receive a broadcast packet if the socket " +
421 "is not bound to a wildcard address; binding to a non-wildcard " +
422 "address (" + localAddress + ") anyway as requested.");
423 }
424
425 boolean wasActive = isActive();
426 try {
427 doBind(localAddress);
428 } catch (Throwable t) {
429 safeSetFailure(promise, t);
430 closeIfClosed();
431 return;
432 }
433
434 if (!wasActive && isActive()) {
435 invokeLater(new Runnable() {
436 @Override
437 public void run() {
438 pipeline.fireChannelActive();
439 }
440 });
441 }
442
443 safeSetSuccess(promise);
444 }
445
446 @Override
447 public final void disconnect(final ChannelPromise promise) {
448 assertEventLoop();
449
450 if (!promise.setUncancellable()) {
451 return;
452 }
453
454 boolean wasActive = isActive();
455 try {
456 doDisconnect();
457
458 remoteAddress = null;
459 localAddress = null;
460 } catch (Throwable t) {
461 safeSetFailure(promise, t);
462 closeIfClosed();
463 return;
464 }
465
466 if (wasActive && !isActive()) {
467 invokeLater(new Runnable() {
468 @Override
469 public void run() {
470 pipeline.fireChannelInactive();
471 }
472 });
473 }
474
475 safeSetSuccess(promise);
476 closeIfClosed();
477 }
478
479 @Override
480 public void close(final ChannelPromise promise) {
481 assertEventLoop();
482
483 ClosedChannelException closedChannelException =
484 StacklessClosedChannelException.newInstance(AbstractChannel.class, "close(ChannelPromise)");
485 close(promise, closedChannelException, closedChannelException, false);
486 }
487
488
489
490
491
492 public final void shutdownOutput(final ChannelPromise promise) {
493 assertEventLoop();
494 shutdownOutput(promise, null);
495 }
496
497
498
499
500
501
502 private void shutdownOutput(final ChannelPromise promise, Throwable cause) {
503 if (!promise.setUncancellable()) {
504 return;
505 }
506
507 final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
508 if (outboundBuffer == null) {
509 promise.setFailure(new ClosedChannelException());
510 return;
511 }
512 this.outboundBuffer = null;
513
514 final Throwable shutdownCause = cause == null ?
515 new ChannelOutputShutdownException("Channel output shutdown") :
516 new ChannelOutputShutdownException("Channel output shutdown", cause);
517
518
519
520
521
522 try {
523
524
525 doShutdownOutput();
526 promise.setSuccess();
527 } catch (Throwable err) {
528 promise.setFailure(err);
529 } finally {
530 closeOutboundBufferForShutdown(pipeline, outboundBuffer, shutdownCause);
531 }
532 }
533
534 private void closeOutboundBufferForShutdown(
535 ChannelPipeline pipeline, ChannelOutboundBuffer buffer, Throwable cause) {
536 buffer.failFlushed(cause, false);
537 buffer.close(cause, true);
538 pipeline.fireUserEventTriggered(ChannelOutputShutdownEvent.INSTANCE);
539 }
540
541 private void close(final ChannelPromise promise, final Throwable cause,
542 final ClosedChannelException closeCause, final boolean notify) {
543 if (!promise.setUncancellable()) {
544 return;
545 }
546
547 if (closeInitiated) {
548 if (closeFuture.isDone()) {
549
550 safeSetSuccess(promise);
551 } else if (!(promise instanceof VoidChannelPromise)) {
552
553 closeFuture.addListener(new ChannelFutureListener() {
554 @Override
555 public void operationComplete(ChannelFuture future) throws Exception {
556 promise.setSuccess();
557 }
558 });
559 }
560 return;
561 }
562
563 closeInitiated = true;
564
565 final boolean wasActive = isActive();
566 final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
567 this.outboundBuffer = null;
568 Executor closeExecutor = prepareToClose();
569 if (closeExecutor != null) {
570 closeExecutor.execute(new Runnable() {
571 @Override
572 public void run() {
573 try {
574
575 doClose0(promise);
576 } finally {
577
578 invokeLater(new Runnable() {
579 @Override
580 public void run() {
581 if (outboundBuffer != null) {
582
583 outboundBuffer.failFlushed(cause, notify);
584 outboundBuffer.close(closeCause);
585 }
586 fireChannelInactiveAndDeregister(wasActive);
587 }
588 });
589 }
590 }
591 });
592 } else {
593 try {
594
595 doClose0(promise);
596 } finally {
597 if (outboundBuffer != null) {
598
599 outboundBuffer.failFlushed(cause, notify);
600 outboundBuffer.close(closeCause);
601 }
602 }
603 if (inFlush0) {
604 invokeLater(new Runnable() {
605 @Override
606 public void run() {
607 fireChannelInactiveAndDeregister(wasActive);
608 }
609 });
610 } else {
611 fireChannelInactiveAndDeregister(wasActive);
612 }
613 }
614 }
615
616 private void doClose0(ChannelPromise promise) {
617 try {
618 doClose();
619 closeFuture.setClosed();
620 safeSetSuccess(promise);
621 } catch (Throwable t) {
622 closeFuture.setClosed();
623 safeSetFailure(promise, t);
624 }
625 }
626
627 private void fireChannelInactiveAndDeregister(final boolean wasActive) {
628 deregister(voidPromise(), wasActive && !isActive());
629 }
630
631 @Override
632 public final void closeForcibly() {
633 assertEventLoop();
634
635 try {
636 doClose();
637 } catch (Exception e) {
638 logger.warn("Failed to close a channel.", e);
639 }
640 }
641
642 @Override
643 public final void deregister(final ChannelPromise promise) {
644 assertEventLoop();
645
646 deregister(promise, false);
647 }
648
649 private void deregister(final ChannelPromise promise, final boolean fireChannelInactive) {
650 if (!promise.setUncancellable()) {
651 return;
652 }
653
654 if (!registered) {
655 safeSetSuccess(promise);
656 return;
657 }
658
659
660
661
662
663
664
665
666
667
668 invokeLater(new Runnable() {
669 @Override
670 public void run() {
671 try {
672 doDeregister();
673 } catch (Throwable t) {
674 logger.warn("Unexpected exception occurred while deregistering a channel.", t);
675 } finally {
676 if (fireChannelInactive) {
677 pipeline.fireChannelInactive();
678 }
679
680
681
682
683 if (registered) {
684 registered = false;
685 pipeline.fireChannelUnregistered();
686 }
687 safeSetSuccess(promise);
688 }
689 }
690 });
691 }
692
693 @Override
694 public final void beginRead() {
695 assertEventLoop();
696
697 try {
698 doBeginRead();
699 } catch (final Exception e) {
700 invokeLater(new Runnable() {
701 @Override
702 public void run() {
703 pipeline.fireExceptionCaught(e);
704 }
705 });
706 close(voidPromise());
707 }
708 }
709
710 @Override
711 public final void write(Object msg, ChannelPromise promise) {
712 assertEventLoop();
713
714 ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
715 if (outboundBuffer == null) {
716 try {
717
718 ReferenceCountUtil.release(msg);
719 } finally {
720
721
722
723
724 safeSetFailure(promise,
725 newClosedChannelException(initialCloseCause, "write(Object, ChannelPromise)"));
726 }
727 return;
728 }
729
730 int size;
731 try {
732 msg = filterOutboundMessage(msg);
733 size = pipeline.estimatorHandle().size(msg);
734 if (size < 0) {
735 size = 0;
736 }
737 } catch (Throwable t) {
738 try {
739 ReferenceCountUtil.release(msg);
740 } finally {
741 safeSetFailure(promise, t);
742 }
743 return;
744 }
745
746 outboundBuffer.addMessage(msg, size, promise);
747 }
748
749 @Override
750 public final void flush() {
751 assertEventLoop();
752
753 ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
754 if (outboundBuffer == null) {
755 return;
756 }
757
758 outboundBuffer.addFlush();
759 flush0();
760 }
761
762 @SuppressWarnings("deprecation")
763 protected void flush0() {
764 if (inFlush0) {
765
766 return;
767 }
768
769 final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
770 if (outboundBuffer == null || outboundBuffer.isEmpty()) {
771 return;
772 }
773
774 inFlush0 = true;
775
776
777 if (!isActive()) {
778 try {
779
780 if (!outboundBuffer.isEmpty()) {
781 if (isOpen()) {
782 outboundBuffer.failFlushed(new NotYetConnectedException(), true);
783 } else {
784
785 outboundBuffer.failFlushed(newClosedChannelException(initialCloseCause, "flush0()"), false);
786 }
787 }
788 } finally {
789 inFlush0 = false;
790 }
791 return;
792 }
793
794 try {
795 doWrite(outboundBuffer);
796 } catch (Throwable t) {
797 handleWriteError(t);
798 } finally {
799 inFlush0 = false;
800 }
801 }
802
803 protected final void handleWriteError(Throwable t) {
804 if (t instanceof IOException && config().isAutoClose()) {
805
806
807
808
809
810
811
812
813 initialCloseCause = t;
814 close(voidPromise(), t, newClosedChannelException(t, "flush0()"), false);
815 } else {
816 try {
817 shutdownOutput(voidPromise(), t);
818 } catch (Throwable t2) {
819 initialCloseCause = t;
820 close(voidPromise(), t2, newClosedChannelException(t, "flush0()"), false);
821 }
822 }
823 }
824
825 private ClosedChannelException newClosedChannelException(Throwable cause, String method) {
826 ClosedChannelException exception =
827 StacklessClosedChannelException.newInstance(AbstractChannel.AbstractUnsafe.class, method);
828 if (cause != null) {
829 exception.initCause(cause);
830 }
831 return exception;
832 }
833
834 @Override
835 public final ChannelPromise voidPromise() {
836 assertEventLoop();
837
838 return unsafeVoidPromise;
839 }
840
841 protected final boolean ensureOpen(ChannelPromise promise) {
842 if (isOpen()) {
843 return true;
844 }
845
846 safeSetFailure(promise, newClosedChannelException(initialCloseCause, "ensureOpen(ChannelPromise)"));
847 return false;
848 }
849
850
851
852
853 protected final void safeSetSuccess(ChannelPromise promise) {
854 if (!(promise instanceof VoidChannelPromise) && !promise.trySuccess()) {
855 logger.warn("Failed to mark a promise as success because it is done already: {}", promise);
856 }
857 }
858
859
860
861
862 protected final void safeSetFailure(ChannelPromise promise, Throwable cause) {
863 if (!(promise instanceof VoidChannelPromise) && !promise.tryFailure(cause)) {
864 logger.warn("Failed to mark a promise as failure because it's done already: {}", promise, cause);
865 }
866 }
867
868 protected final void closeIfClosed() {
869 if (isOpen()) {
870 return;
871 }
872 close(voidPromise());
873 }
874
875 private void invokeLater(Runnable task) {
876 try {
877
878
879
880
881
882
883
884
885
886
887
888 eventLoop().execute(task);
889 } catch (RejectedExecutionException e) {
890 logger.warn("Can't invoke task later as EventLoop rejected it", e);
891 }
892 }
893
894
895
896
897 protected final Throwable annotateConnectException(Throwable cause, SocketAddress remoteAddress) {
898 if (cause instanceof ConnectException) {
899 return new AnnotatedConnectException((ConnectException) cause, remoteAddress);
900 }
901 if (cause instanceof NoRouteToHostException) {
902 return new AnnotatedNoRouteToHostException((NoRouteToHostException) cause, remoteAddress);
903 }
904 if (cause instanceof SocketException) {
905 return new AnnotatedSocketException((SocketException) cause, remoteAddress);
906 }
907
908 return cause;
909 }
910
911
912
913
914
915
916
917 protected Executor prepareToClose() {
918 return null;
919 }
920 }
921
922
923
924
925 protected abstract boolean isCompatible(EventLoop loop);
926
927
928
929
930 protected abstract SocketAddress localAddress0();
931
932
933
934
935 protected abstract SocketAddress remoteAddress0();
936
937
938
939
940
941
942
943 @Deprecated
944 protected void doRegister() throws Exception {
945
946 }
947
948
949
950
951
952
953
954 protected void doRegister(ChannelPromise promise) {
955 try {
956 doRegister();
957 } catch (Throwable cause) {
958 promise.setFailure(cause);
959 return;
960 }
961 promise.setSuccess();
962 }
963
964
965
966
967 protected abstract void doBind(SocketAddress localAddress) throws Exception;
968
969
970
971
972 protected abstract void doDisconnect() throws Exception;
973
974
975
976
977 protected abstract void doClose() throws Exception;
978
979
980
981
982
983 protected void doShutdownOutput() throws Exception {
984 doClose();
985 }
986
987
988
989
990
991
992 protected void doDeregister() throws Exception {
993
994 }
995
996
997
998
999 protected abstract void doBeginRead() throws Exception;
1000
1001
1002
1003
1004 protected abstract void doWrite(ChannelOutboundBuffer in) throws Exception;
1005
1006
1007
1008
1009
1010 protected Object filterOutboundMessage(Object msg) throws Exception {
1011 return msg;
1012 }
1013
1014 protected void validateFileRegion(DefaultFileRegion region, long position) throws IOException {
1015 DefaultFileRegion.validate(region, position);
1016 }
1017
1018 static final class CloseFuture extends DefaultChannelPromise {
1019
1020 CloseFuture(AbstractChannel ch) {
1021 super(ch);
1022 }
1023
1024 @Override
1025 public ChannelPromise setSuccess() {
1026 throw new IllegalStateException();
1027 }
1028
1029 @Override
1030 public ChannelPromise setFailure(Throwable cause) {
1031 throw new IllegalStateException();
1032 }
1033
1034 @Override
1035 public boolean trySuccess() {
1036 throw new IllegalStateException();
1037 }
1038
1039 @Override
1040 public boolean tryFailure(Throwable cause) {
1041 throw new IllegalStateException();
1042 }
1043
1044 boolean setClosed() {
1045 return super.trySuccess();
1046 }
1047 }
1048
1049 private static final class AnnotatedConnectException extends ConnectException {
1050
1051 private static final long serialVersionUID = 3901958112696433556L;
1052
1053 AnnotatedConnectException(ConnectException exception, SocketAddress remoteAddress) {
1054 super(exception.getMessage() + ": " + remoteAddress);
1055 initCause(exception);
1056 }
1057
1058
1059 @Override
1060 public Throwable fillInStackTrace() {
1061 return this;
1062 }
1063 }
1064
1065 private static final class AnnotatedNoRouteToHostException extends NoRouteToHostException {
1066
1067 private static final long serialVersionUID = -6801433937592080623L;
1068
1069 AnnotatedNoRouteToHostException(NoRouteToHostException exception, SocketAddress remoteAddress) {
1070 super(exception.getMessage() + ": " + remoteAddress);
1071 initCause(exception);
1072 }
1073
1074
1075 @Override
1076 public Throwable fillInStackTrace() {
1077 return this;
1078 }
1079 }
1080
1081 private static final class AnnotatedSocketException extends SocketException {
1082
1083 private static final long serialVersionUID = 3896743275010454039L;
1084
1085 AnnotatedSocketException(SocketException exception, SocketAddress remoteAddress) {
1086 super(exception.getMessage() + ": " + remoteAddress);
1087 initCause(exception);
1088 }
1089
1090
1091 @Override
1092 public Throwable fillInStackTrace() {
1093 return this;
1094 }
1095 }
1096 }