1 /*
2  * JBoss, Home of Professional Open Source
3  *
4  * Copyright 2014 Red Hat, Inc. and/or its affiliates.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19
20 package org.xnio.nio;
21
22 import java.io.Closeable;
23 import java.io.IOException;
24 import java.nio.channels.Selector;
25 import java.nio.channels.spi.SelectorProvider;
26 import java.security.AccessController;
27 import java.security.PrivilegedAction;
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.InvocationTargetException;
30 import org.xnio.FileSystemWatcher;
31 import org.xnio.IoUtils;
32 import org.xnio.Options;
33 import org.xnio.ReadPropertyAction;
34 import org.xnio.Xnio;
35 import org.xnio.OptionMap;
36 import org.xnio.XnioWorker;
37 import org.xnio.management.XnioProviderMXBean;
38 import org.xnio.management.XnioServerMXBean;
39 import org.xnio.management.XnioWorkerMXBean;
40
41 import static org.xnio.nio.Log.log;
42
43 /**
44  * An NIO-based XNIO provider for a standalone application.
45  */

46 final class NioXnio extends Xnio {
47
48     static final boolean IS_HP_UX;
49     static final boolean HAS_BUGGY_EVENT_PORT;
50
51     interface SelectorCreator {
52         Selector open() throws IOException;
53     }
54
55     final SelectorCreator tempSelectorCreator;
56     final SelectorCreator mainSelectorCreator;
57
58     static {
59         log.greeting(Version.getVersionString());
60         IS_HP_UX = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
61             public Boolean run() {
62                 final String bugLevel = System.getProperty("sun.nio.ch.bugLevel");
63                 if (bugLevel == null) System.setProperty("sun.nio.ch.bugLevel""");
64                 return Boolean.valueOf(System.getProperty("os.name""unknown").equalsIgnoreCase("hp-ux"));
65             }
66         }).booleanValue();
67         // if a JDK is released with a fix, we can try to detect it and set this to "false" for those JDKs.
68         HAS_BUGGY_EVENT_PORT = true;
69     }
70
71     /**
72      * Construct a new NIO-based XNIO provider instance.  Should only be invoked by the service loader.
73      */

74     NioXnio() {
75         super("nio");
76         final Object[] objects = AccessController.doPrivileged(
77             new PrivilegedAction<Object[]>() {
78                 public Object[] run() {
79                     String jdkVersion = System.getProperty("java.specification.version""1.8");
80                     final boolean jdk9 = ! (jdkVersion.equals("1.8") || jdkVersion.equals("8"));
81                     final SelectorProvider defaultProvider = SelectorProvider.provider();
82                     final String chosenProvider = System.getProperty("xnio.nio.selector.provider");
83                     SelectorProvider provider = null;
84                     if (chosenProvider != null) {
85                         try {
86                             provider = Class.forName(chosenProvider, true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
87                             provider.openSelector().close();
88                         } catch (Throwable e) {
89                             // not available
90                             provider = null;
91                         }
92                     }
93                     if (! jdk9) {
94                         // try to probe the best available provider
95                         if (provider == null) {
96                             try {
97                                 // Mac OS X and BSD
98                                 provider = Class.forName("sun.nio.ch.KQueueSelectorProvider"true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
99                                 provider.openSelector().close();
100                             } catch (Throwable e) {
101                                 // not available
102                                 provider = null;
103                             }
104                         }
105                         if (provider == null) {
106                             try {
107                                 // Linux
108                                 provider = Class.forName("sun.nio.ch.EPollSelectorProvider"true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
109                                 provider.openSelector().close();
110                             } catch (Throwable e) {
111                                 // not available
112                                 provider = null;
113                             }
114                         }
115                         if (provider == null && ! HAS_BUGGY_EVENT_PORT) {
116                             try {
117                                 // Solaris (Java 8+)
118                                 provider = Class.forName("sun.nio.ch.EventPortSelectorProvider"true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
119                                 provider.openSelector().close();
120                             } catch (Throwable e) {
121                                 // not available
122                                 provider = null;
123                             }
124                         }
125                         if (provider == null) {
126                             try {
127                                 // Solaris
128                                 provider = Class.forName("sun.nio.ch.DevPollSelectorProvider"true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
129                                 provider.openSelector().close();
130                             } catch (Throwable e) {
131                                 // not available
132                                 provider = null;
133                             }
134                         }
135                         if (provider == null) {
136                             try {
137                                 // Solaris (Java 8+)
138                                 provider = Class.forName("sun.nio.ch.EventPortSelectorProvider"true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
139                                 provider.openSelector().close();
140                             } catch (Throwable e) {
141                                 // not available
142                                 provider = null;
143                             }
144                         }
145                         if (provider == null) {
146                             try {
147                                 // AIX
148                                 provider = Class.forName("sun.nio.ch.PollsetSelectorProvider"true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
149                                 provider.openSelector().close();
150                             } catch (Throwable e) {
151                                 // not available
152                                 provider = null;
153                             }
154                         }
155                     }
156                     if (provider == null) {
157                         try {
158                             defaultProvider.openSelector().close();
159                             provider = defaultProvider;
160                         } catch (Throwable e) {
161                             // not available
162                         }
163                     }
164                     if (provider == null) {
165                         try {
166                             // Nothing else works, not even the default
167                             provider = Class.forName("sun.nio.ch.PollSelectorProvider"true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
168                             provider.openSelector().close();
169                         } catch (Throwable e) {
170                             // not available
171                             provider = null;
172                         }
173                     }
174                     if (provider == null) {
175                         throw log.noSelectorProvider();
176                     }
177                     log.selectorProvider(provider);
178                     final boolean defaultIsPoll = "sun.nio.ch.PollSelectorProvider".equals(provider.getClass().getName());
179                     final String chosenMainSelector = System.getProperty("xnio.nio.selector.main");
180                     final String chosenTempSelector = System.getProperty("xnio.nio.selector.temp");
181                     final SelectorCreator defaultSelectorCreator = new DefaultSelectorCreator(provider);
182                     final Object[] objects = new Object[3];
183                     objects[0] = provider;
184                     if (chosenTempSelector != nulltry {
185                         final ConstructorSelectorCreator creator = new ConstructorSelectorCreator(chosenTempSelector, provider);
186                         IoUtils.safeClose(creator.open());
187                         objects[1] = creator;
188                     } catch (Exception e) {
189                         // not available
190                     }
191                     if (chosenMainSelector != nulltry {
192                         final ConstructorSelectorCreator creator = new ConstructorSelectorCreator(chosenMainSelector, provider);
193                         IoUtils.safeClose(creator.open());
194                         objects[2] = creator;
195                     } catch (Exception e) {
196                         // not available
197                     }
198                     if (! defaultIsPoll && ! jdk9) {
199                         // default is fine for main selectors; we should try to get poll for temp though
200                         if (objects[1] == nulltry {
201                             SelectorProvider pollSelectorProvider = Class.forName("sun.nio.ch.PollSelectorProvider"true, NioXnio.class.getClassLoader()).asSubclass(SelectorProvider.class).getConstructor().newInstance();
202                             pollSelectorProvider.openSelector().close();
203                             objects[1] = new DefaultSelectorCreator(provider);
204                         } catch (Exception e) {
205                             // not available
206                         }
207                     }
208                     if (objects[1] == null) {
209                         objects[1] = defaultSelectorCreator;
210                     }
211                     if (objects[2] == null) {
212                         objects[2] = defaultSelectorCreator;
213                     }
214                     return objects;
215                 }
216             }
217         );
218         tempSelectorCreator = (SelectorCreator) objects[1];
219         mainSelectorCreator = (SelectorCreator) objects[2];
220         log.selectors(mainSelectorCreator, tempSelectorCreator);
221         register(new XnioProviderMXBean() {
222             public String getName() {
223                 return "nio";
224             }
225
226             public String getVersion() {
227                 return Version.getVersionString();
228             }
229         });
230     }
231
232     protected XnioWorker build(final XnioWorker.Builder builder) {
233         final NioXnioWorker worker = new NioXnioWorker(builder);
234         worker.start();
235         return worker;
236     }
237
238     @Override
239     public FileSystemWatcher createFileSystemWatcher(String name, OptionMap options) {
240         try {
241             boolean daemonThread = options.get(Options.THREAD_DAEMON, true);
242             return new WatchServiceFileSystemWatcher(name, daemonThread);
243         } catch (LinkageError e) {
244             //ignore
245         }
246         return super.createFileSystemWatcher(name, options);
247     }
248
249     private final ThreadLocal<FinalizableSelectorHolder> selectorThreadLocal = new ThreadLocal<FinalizableSelectorHolder>() {
250         public void remove() {
251             // if no selector was created, none will be closed
252             FinalizableSelectorHolder holder = get();
253             if(holder != null) {
254                 IoUtils.safeClose(holder.selector);
255             }
256             super.remove();
257         }
258     };
259
260     Selector getSelector() throws IOException {
261         final ThreadLocal<FinalizableSelectorHolder> threadLocal = selectorThreadLocal;
262         FinalizableSelectorHolder holder = threadLocal.get();
263         if (holder == null) {
264             holder = new FinalizableSelectorHolder(tempSelectorCreator.open());
265             threadLocal.set(holder);
266         }
267         return holder.selector;
268     }
269
270     private static class DefaultSelectorCreator implements SelectorCreator {
271         private final SelectorProvider provider;
272
273         private DefaultSelectorCreator(final SelectorProvider provider) {
274             this.provider = provider;
275         }
276
277         public Selector open() throws IOException {
278             return provider.openSelector();
279         }
280
281         public String toString() {
282             return "Default system selector creator for provider " + provider.getClass();
283         }
284     }
285
286     private static class ConstructorSelectorCreator implements SelectorCreator {
287
288         private final Constructor<? extends Selector> constructor;
289         private final SelectorProvider provider;
290
291         public ConstructorSelectorCreator(final String name, final SelectorProvider provider) throws ClassNotFoundException, NoSuchMethodException {
292             this.provider = provider;
293             final Class<? extends Selector> selectorImplClass = Class.forName(name, truenull).asSubclass(Selector.class);
294             final Constructor<? extends Selector> constructor = selectorImplClass.getDeclaredConstructor(SelectorProvider.class);
295             constructor.setAccessible(true);
296             this.constructor = constructor;
297         }
298
299         public Selector open() throws IOException {
300             try {
301                 return constructor.newInstance(provider);
302             } catch (InstantiationException e) {
303                 return Selector.open();
304             } catch (IllegalAccessException e) {
305                 return Selector.open();
306             } catch (InvocationTargetException e) {
307                 try {
308                     throw e.getTargetException();
309                 } catch (IOException | Error | RuntimeException e2) {
310                     throw e2;
311                 } catch (Throwable t) {
312                     throw log.unexpectedSelectorOpenProblem(t);
313                 }
314             }
315         }
316
317         public String toString() {
318             return String.format("Selector creator %s for provider %s", constructor.getDeclaringClass(), provider.getClass());
319         }
320     }
321
322     protected static Closeable register(XnioWorkerMXBean workerMXBean) {
323         return Xnio.register(workerMXBean);
324     }
325
326     protected static Closeable register(XnioServerMXBean serverMXBean) {
327         return Xnio.register(serverMXBean);
328     }
329
330     private static final class FinalizableSelectorHolder {
331         final Selector selector;
332
333
334         private FinalizableSelectorHolder(Selector selector) {
335             this.selector = selector;
336         }
337
338         @Override
339         protected void finalize() throws Throwable {
340             IoUtils.safeClose(selector);
341         }
342     }
343 }
344