View Javadoc
1   /*
2    * Copyright 2022 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    *   https://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.netty5.channel.socket.nio;
17  
18  import io.netty5.channel.socket.DomainSocketAddress;
19  import io.netty5.channel.socket.SocketProtocolFamily;
20  import io.netty5.util.internal.PlatformDependent;
21  import io.netty5.util.internal.logging.InternalLogger;
22  import io.netty5.util.internal.logging.InternalLoggerFactory;
23  
24  import java.io.IOException;
25  import java.lang.invoke.MethodHandle;
26  import java.lang.invoke.MethodHandles;
27  import java.lang.invoke.MethodType;
28  import java.lang.reflect.InvocationTargetException;
29  import java.lang.reflect.Method;
30  import java.net.ProtocolFamily;
31  import java.net.SocketAddress;
32  import java.net.StandardProtocolFamily;
33  import java.nio.channels.Channel;
34  import java.nio.channels.spi.SelectorProvider;
35  import java.nio.file.Path;
36  
37  final class NioChannelUtil {
38      private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioChannelUtil.class);
39  
40      private static final MethodHandle OF_METHOD_HANDLE;
41      private static final MethodHandle GET_PATH_METHOD_HANDLE;
42  
43      static {
44          MethodHandle ofMethodHandle = null;
45          MethodHandle getPathMethodHandle = null;
46  
47          if (PlatformDependent.javaVersion() >= 16) {
48              try {
49                  Class<?> clazz = Class.forName("java.net.UnixDomainSocketAddress", false,
50                          PlatformDependent.getClassLoader(NioChannelUtil.class));
51                  MethodHandles.Lookup lookup = MethodHandles.lookup();
52                  MethodType type = MethodType.methodType(clazz, String.class);
53                  ofMethodHandle = lookup.findStatic(clazz, "of", type);
54  
55                  type = MethodType.methodType(Path.class);
56                  getPathMethodHandle = lookup.findVirtual(clazz, "getPath", type);
57              } catch (ClassNotFoundException t) {
58                  ofMethodHandle = null;
59                  getPathMethodHandle = null;
60                  logger.debug(
61                          "java.net.UnixDomainSocketAddress not found", t);
62              } catch (NoSuchMethodException | IllegalAccessException e) {
63                  ofMethodHandle = null;
64                  getPathMethodHandle = null;
65                  logger.debug(
66                          "Could not access methods of java.net.UnixDomainSocketAddress", e);
67              }
68          }
69          OF_METHOD_HANDLE = ofMethodHandle;
70          GET_PATH_METHOD_HANDLE = getPathMethodHandle;
71      }
72  
73      static boolean isDomainSocket(ProtocolFamily family) {
74          if (family instanceof StandardProtocolFamily) {
75              return "UNIX".equals(family.name());
76          }
77          if (family instanceof SocketProtocolFamily) {
78              return family == SocketProtocolFamily.UNIX;
79          }
80          return false;
81      }
82  
83      static SocketAddress toDomainSocketAddress(SocketAddress address) {
84          if (GET_PATH_METHOD_HANDLE != null) {
85              try {
86                  Path path = (Path) GET_PATH_METHOD_HANDLE.invoke(address);
87                  return new DomainSocketAddress(path.toFile());
88              } catch (RuntimeException e) {
89                  throw e;
90              } catch (Throwable cause) {
91                  return null;
92              }
93          }
94          return address;
95      }
96  
97      static SocketAddress toUnixDomainSocketAddress(SocketAddress address) {
98          if (OF_METHOD_HANDLE != null) {
99              if (address instanceof DomainSocketAddress) {
100                 try {
101                     return (SocketAddress) OF_METHOD_HANDLE.invoke(((DomainSocketAddress) address).path());
102                 } catch (RuntimeException e) {
103                     throw e;
104                 } catch (Throwable cause) {
105                     return null;
106                 }
107             }
108         }
109         return address;
110     }
111 
112     static Method findOpenMethod(String methodName) {
113         if (PlatformDependent.javaVersion() >= 15) {
114             try {
115                 return SelectorProvider.class.getMethod(methodName, ProtocolFamily.class);
116             } catch (Throwable e) {
117                 logger.debug("SelectorProvider.{}(ProtocolFamily) not available, will use default", methodName, e);
118             }
119         }
120         return null;
121     }
122 
123     static <C extends Channel> C newChannel(Method method, SelectorProvider provider,
124                                             ProtocolFamily family) throws IOException {
125         /*
126          *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
127          *  {@link SelectorProvider#provider()} which is called by each SocketChannel.open() otherwise.
128          *
129          *  See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
130          */
131         if (family != null && method != null) {
132             try {
133                 @SuppressWarnings("unchecked")
134                 C channel = (C) method.invoke(
135                         provider, family);
136                 return channel;
137             } catch (InvocationTargetException | IllegalAccessException e) {
138                 throw new IOException(e);
139             }
140         }
141         return null;
142     }
143 
144     static ProtocolFamily toJdkFamily(ProtocolFamily family) {
145         if (family instanceof SocketProtocolFamily) {
146             return ((SocketProtocolFamily) family).toJdkFamily();
147         }
148         return family;
149     }
150 
151     private NioChannelUtil() { }
152 }