View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.unix;
17  
18  import io.netty.util.internal.EmptyArrays;
19  
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.net.ConnectException;
23  import java.net.NoRouteToHostException;
24  import java.nio.channels.AlreadyConnectedException;
25  import java.nio.channels.ClosedChannelException;
26  import java.nio.channels.ConnectionPendingException;
27  import java.nio.channels.NotYetConnectedException;
28  
29  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.*;
30  
31  /**
32   * <strong>Internal usage only!</strong>
33   * <p>Static members which call JNI methods must be defined in {@link ErrorsStaticallyReferencedJniMethods}.
34   */
35  public final class Errors {
36      // As all our JNI methods return -errno on error we need to compare with the negative errno codes.
37      public static final int ERRNO_ENOENT_NEGATIVE = -errnoENOENT();
38      public static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN();
39      public static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
40      public static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
41      public static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
42      public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
43      public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
44      public static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS();
45      public static final int ERROR_ECONNREFUSED_NEGATIVE = -errorECONNREFUSED();
46      public static final int ERROR_EISCONN_NEGATIVE = -errorEISCONN();
47      public static final int ERROR_EALREADY_NEGATIVE = -errorEALREADY();
48      public static final int ERROR_ENETUNREACH_NEGATIVE = -errorENETUNREACH();
49  
50      /**
51       * Holds the mappings for errno codes to String messages.
52       * This eliminates the need to call back into JNI to get the right String message on an exception
53       * and thus is faster.
54       *
55       * The array length of 512 should be more then enough because errno.h only holds < 200 codes.
56       */
57      private static final String[] ERRORS = new String[512];
58  
59      /**
60       * <strong>Internal usage only!</strong>
61       */
62      public static final class NativeIoException extends IOException {
63          private static final long serialVersionUID = 8222160204268655526L;
64          private final int expectedErr;
65          public NativeIoException(String method, int expectedErr) {
66              super(method + "(..) failed: " + ERRORS[-expectedErr]);
67              this.expectedErr = expectedErr;
68          }
69  
70          public int expectedErr() {
71              return expectedErr;
72          }
73      }
74  
75      static final class NativeConnectException extends ConnectException {
76          private static final long serialVersionUID = -5532328671712318161L;
77          private final int expectedErr;
78          NativeConnectException(String method, int expectedErr) {
79              super(method + "(..) failed: " + ERRORS[-expectedErr]);
80              this.expectedErr = expectedErr;
81          }
82  
83          int expectedErr() {
84              return expectedErr;
85          }
86      }
87  
88      static {
89          for (int i = 0; i < ERRORS.length; i++) {
90              // This is ok as strerror returns 'Unknown error i' when the message is not known.
91              ERRORS[i] = strError(i);
92          }
93      }
94  
95      static void throwConnectException(String method, NativeConnectException refusedCause, int err)
96              throws IOException {
97          if (err == refusedCause.expectedErr()) {
98              throw refusedCause;
99          }
100         if (err == ERROR_EALREADY_NEGATIVE) {
101             throw new ConnectionPendingException();
102         }
103         if (err == ERROR_ENETUNREACH_NEGATIVE) {
104             throw new NoRouteToHostException();
105         }
106         if (err == ERROR_EISCONN_NEGATIVE) {
107             throw new AlreadyConnectedException();
108         }
109         if (err == ERRNO_ENOENT_NEGATIVE) {
110             throw new FileNotFoundException();
111         }
112         throw new ConnectException(method + "(..) failed: " + ERRORS[-err]);
113     }
114 
115     public static NativeIoException newConnectionResetException(String method, int errnoNegative) {
116         NativeIoException exception = newIOException(method, errnoNegative);
117         exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
118         return exception;
119     }
120 
121     public static NativeIoException newIOException(String method, int err) {
122         return new NativeIoException(method, err);
123     }
124 
125     public static int ioResult(String method, int err, NativeIoException resetCause,
126                                ClosedChannelException closedCause) throws IOException {
127         // network stack saturated... try again later
128         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
129             return 0;
130         }
131         if (err == resetCause.expectedErr()) {
132             throw resetCause;
133         }
134         if (err == ERRNO_EBADF_NEGATIVE) {
135             throw closedCause;
136         }
137         if (err == ERRNO_ENOTCONN_NEGATIVE) {
138             throw new NotYetConnectedException();
139         }
140         if (err == ERRNO_ENOENT_NEGATIVE) {
141             throw new FileNotFoundException();
142         }
143 
144         // TODO: We could even go further and use a pre-instantiated IOException for the other error codes, but for
145         //       all other errors it may be better to just include a stack trace.
146         throw newIOException(method, err);
147     }
148 
149     private Errors() { }
150 }