View Javadoc
1   /*
2    * Copyright 2012 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.netty.channel.socket.nio;
17  
18  import io.netty.channel.ChannelException;
19  import io.netty.channel.ChannelOption;
20  import io.netty.channel.socket.DatagramChannelConfig;
21  import io.netty.channel.socket.DefaultDatagramChannelConfig;
22  import io.netty.util.internal.PlatformDependent;
23  import io.netty.util.internal.SocketUtils;
24  
25  import java.lang.reflect.Method;
26  import java.net.InetAddress;
27  import java.net.NetworkInterface;
28  import java.net.SocketException;
29  import java.nio.channels.DatagramChannel;
30  import java.util.Enumeration;
31  import java.util.Map;
32  
33  /**
34   * The default {@link NioDatagramChannelConfig} implementation.
35   */
36  class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
37  
38      private static final Object IP_MULTICAST_TTL;
39      private static final Object IP_MULTICAST_IF;
40      private static final Object IP_MULTICAST_LOOP;
41      private static final Method GET_OPTION;
42      private static final Method SET_OPTION;
43  
44      static {
45          ClassLoader classLoader = PlatformDependent.getClassLoader(DatagramChannel.class);
46          Class<?> socketOptionType = null;
47          try {
48              socketOptionType = Class.forName("java.net.SocketOption", true, classLoader);
49          } catch (Exception e) {
50              // Not Java 7+
51          }
52          Class<?> stdSocketOptionType = null;
53          try {
54              stdSocketOptionType = Class.forName("java.net.StandardSocketOptions", true, classLoader);
55          } catch (Exception e) {
56              // Not Java 7+
57          }
58  
59          Object ipMulticastTtl = null;
60          Object ipMulticastIf = null;
61          Object ipMulticastLoop = null;
62          Method getOption = null;
63          Method setOption = null;
64          if (socketOptionType != null) {
65              try {
66                  ipMulticastTtl = stdSocketOptionType.getDeclaredField("IP_MULTICAST_TTL").get(null);
67              } catch (Exception e) {
68                  throw new Error("cannot locate the IP_MULTICAST_TTL field", e);
69              }
70  
71              try {
72                  ipMulticastIf = stdSocketOptionType.getDeclaredField("IP_MULTICAST_IF").get(null);
73              } catch (Exception e) {
74                  throw new Error("cannot locate the IP_MULTICAST_IF field", e);
75              }
76  
77              try {
78                  ipMulticastLoop = stdSocketOptionType.getDeclaredField("IP_MULTICAST_LOOP").get(null);
79              } catch (Exception e) {
80                  throw new Error("cannot locate the IP_MULTICAST_LOOP field", e);
81              }
82  
83              Class<?> networkChannelClass = null;
84              try {
85                  networkChannelClass = Class.forName("java.nio.channels.NetworkChannel", true, classLoader);
86              } catch (Throwable ignore) {
87                  // Not Java 7+
88              }
89  
90              if (networkChannelClass == null) {
91                  getOption = null;
92                  setOption = null;
93              } else {
94                  try {
95                      getOption = networkChannelClass.getDeclaredMethod("getOption", socketOptionType);
96                  } catch (Exception e) {
97                      throw new Error("cannot locate the getOption() method", e);
98                  }
99  
100                 try {
101                     setOption = networkChannelClass.getDeclaredMethod("setOption", socketOptionType, Object.class);
102                 } catch (Exception e) {
103                     throw new Error("cannot locate the setOption() method", e);
104                 }
105             }
106         }
107         IP_MULTICAST_TTL = ipMulticastTtl;
108         IP_MULTICAST_IF = ipMulticastIf;
109         IP_MULTICAST_LOOP = ipMulticastLoop;
110         GET_OPTION = getOption;
111         SET_OPTION = setOption;
112     }
113 
114     private final DatagramChannel javaChannel;
115 
116     NioDatagramChannelConfig(NioDatagramChannel channel, DatagramChannel javaChannel) {
117         super(channel, javaChannel.socket());
118         this.javaChannel = javaChannel;
119     }
120 
121     @Override
122     public int getTimeToLive() {
123         return (Integer) getOption0(IP_MULTICAST_TTL);
124     }
125 
126     @Override
127     public DatagramChannelConfig setTimeToLive(int ttl) {
128         setOption0(IP_MULTICAST_TTL, ttl);
129         return this;
130     }
131 
132     @Override
133     public InetAddress getInterface() {
134         NetworkInterface inf = getNetworkInterface();
135         if (inf != null) {
136             Enumeration<InetAddress> addresses = SocketUtils.addressesFromNetworkInterface(inf);
137             if (addresses.hasMoreElements()) {
138                 return addresses.nextElement();
139             }
140         }
141         return null;
142     }
143 
144     @Override
145     public DatagramChannelConfig setInterface(InetAddress interfaceAddress) {
146         try {
147             setNetworkInterface(NetworkInterface.getByInetAddress(interfaceAddress));
148         } catch (SocketException e) {
149             throw new ChannelException(e);
150         }
151         return this;
152     }
153 
154     @Override
155     public NetworkInterface getNetworkInterface() {
156         return (NetworkInterface) getOption0(IP_MULTICAST_IF);
157     }
158 
159     @Override
160     public DatagramChannelConfig setNetworkInterface(NetworkInterface networkInterface) {
161         setOption0(IP_MULTICAST_IF, networkInterface);
162         return this;
163     }
164 
165     @Override
166     public boolean isLoopbackModeDisabled() {
167         return (Boolean) getOption0(IP_MULTICAST_LOOP);
168     }
169 
170     @Override
171     public DatagramChannelConfig setLoopbackModeDisabled(boolean loopbackModeDisabled) {
172         setOption0(IP_MULTICAST_LOOP, loopbackModeDisabled);
173         return this;
174     }
175 
176     @Override
177     public DatagramChannelConfig setAutoRead(boolean autoRead) {
178         super.setAutoRead(autoRead);
179         return this;
180     }
181 
182     @Override
183     protected void autoReadCleared() {
184         ((NioDatagramChannel) channel).clearReadPending0();
185     }
186 
187     private Object getOption0(Object option) {
188         if (GET_OPTION == null) {
189             throw new UnsupportedOperationException();
190         } else {
191             try {
192                 return GET_OPTION.invoke(javaChannel, option);
193             } catch (Exception e) {
194                 throw new ChannelException(e);
195             }
196         }
197     }
198 
199     private void setOption0(Object option, Object value) {
200         if (SET_OPTION == null) {
201             throw new UnsupportedOperationException();
202         } else {
203             try {
204                 SET_OPTION.invoke(javaChannel, option, value);
205             } catch (Exception e) {
206                 throw new ChannelException(e);
207             }
208         }
209     }
210 
211     @Override
212     public <T> boolean setOption(ChannelOption<T> option, T value) {
213         if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) {
214             return NioChannelOption.setOption(javaChannel, (NioChannelOption<T>) option, value);
215         }
216         return super.setOption(option, value);
217     }
218 
219     @Override
220     public <T> T getOption(ChannelOption<T> option) {
221         if (PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption) {
222             return NioChannelOption.getOption(javaChannel, (NioChannelOption<T>) option);
223         }
224         return super.getOption(option);
225     }
226 
227     @SuppressWarnings("unchecked")
228     @Override
229     public Map<ChannelOption<?>, Object> getOptions() {
230         if (PlatformDependent.javaVersion() >= 7) {
231             return getOptions(super.getOptions(), NioChannelOption.getOptions(javaChannel));
232         }
233         return super.getOptions();
234     }
235 }