1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.channel.socket.nio;
17
18 import org.jboss.netty.logging.InternalLogger;
19 import org.jboss.netty.logging.InternalLoggerFactory;
20 import org.jboss.netty.util.internal.SystemPropertyUtil;
21
22 import java.io.IOException;
23 import java.net.InetSocketAddress;
24 import java.nio.channels.SelectionKey;
25 import java.nio.channels.Selector;
26 import java.nio.channels.ServerSocketChannel;
27 import java.nio.channels.spi.SelectorProvider;
28 import java.util.Map.Entry;
29 import java.util.Set;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.TimeUnit;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36
37
38
39
40 final class NioProviderMetadata {
41 static final InternalLogger logger =
42 InternalLoggerFactory.getInstance(NioProviderMetadata.class);
43
44 private static final String CONSTRAINT_LEVEL_PROPERTY =
45 "org.jboss.netty.channel.socket.nio.constraintLevel";
46
47 private static final String OLD_CONSTRAINT_LEVEL_PROPERTY =
48 "java.nio.channels.spi.constraintLevel";
49
50
51
52
53
54
55 static final int CONSTRAINT_LEVEL;
56
57 static {
58 int constraintLevel = -1;
59
60
61 constraintLevel = SystemPropertyUtil.getInt(CONSTRAINT_LEVEL_PROPERTY, -1);
62 if (constraintLevel < 0 || constraintLevel > 2) {
63
64 constraintLevel = SystemPropertyUtil.getInt(OLD_CONSTRAINT_LEVEL_PROPERTY, -1);
65 if (constraintLevel < 0 || constraintLevel > 2) {
66 constraintLevel = -1;
67 } else {
68 logger.warn(
69 "System property '" +
70 OLD_CONSTRAINT_LEVEL_PROPERTY +
71 "' has been deprecated. Use '" +
72 CONSTRAINT_LEVEL_PROPERTY + "' instead.");
73 }
74 }
75
76 if (constraintLevel >= 0) {
77 logger.debug(
78 "Setting the NIO constraint level to: " + constraintLevel);
79 }
80
81 if (constraintLevel < 0) {
82 constraintLevel = detectConstraintLevelFromSystemProperties();
83
84 if (constraintLevel < 0) {
85 constraintLevel = 2;
86 if (logger.isDebugEnabled()) {
87 logger.debug(
88 "Couldn't determine the NIO constraint level from " +
89 "the system properties; using the safest level (2)");
90 }
91 } else if (constraintLevel != 0) {
92 if (logger.isInfoEnabled()) {
93 logger.info(
94 "Using the autodetected NIO constraint level: " +
95 constraintLevel +
96 " (Use better NIO provider for better performance)");
97 }
98 } else {
99 if (logger.isDebugEnabled()) {
100 logger.debug(
101 "Using the autodetected NIO constraint level: " +
102 constraintLevel);
103 }
104
105 }
106 }
107
108 CONSTRAINT_LEVEL = constraintLevel;
109
110 if (CONSTRAINT_LEVEL < 0 || CONSTRAINT_LEVEL > 2) {
111 throw new Error(
112 "Unexpected NIO constraint level: " +
113 CONSTRAINT_LEVEL + ", please report this error.");
114 }
115 }
116
117 private static int detectConstraintLevelFromSystemProperties() {
118 String version = SystemPropertyUtil.get("java.specification.version");
119 String vminfo = SystemPropertyUtil.get("java.vm.info", "");
120 String os = SystemPropertyUtil.get("os.name");
121 String vendor = SystemPropertyUtil.get("java.vm.vendor");
122 String provider;
123 try {
124 provider = SelectorProvider.provider().getClass().getName();
125 } catch (Exception e) {
126
127 provider = null;
128 }
129
130 if (version == null || os == null || vendor == null || provider == null) {
131 return -1;
132 }
133
134 os = os.toLowerCase();
135 vendor = vendor.toLowerCase();
136
137
138
139
140
141
142
143
144 if (vendor.contains("sun")) {
145
146 if (os.contains("linux")) {
147 if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
148 provider.equals("sun.nio.ch.PollSelectorProvider")) {
149 return 0;
150 }
151
152
153 } else if (os.contains("windows")) {
154 if (provider.equals("sun.nio.ch.WindowsSelectorProvider")) {
155 return 0;
156 }
157
158
159 } else if (os.contains("sun") || os.contains("solaris")) {
160 if (provider.equals("sun.nio.ch.DevPollSelectorProvider")) {
161 return 0;
162 }
163 }
164
165 } else if (vendor.contains("apple")) {
166
167 if (os.contains("mac") && os.contains("os")) {
168 if (provider.equals("sun.nio.ch.KQueueSelectorProvider")) {
169 return 0;
170 }
171 }
172
173 } else if (vendor.contains("ibm")) {
174
175 if (os.contains("linux") || os.contains("aix")) {
176 if (version.equals("1.5") || version.matches("^1\\.5\\D.*$")) {
177 if (provider.equals("sun.nio.ch.PollSelectorProvider")) {
178 return 1;
179 }
180 } else if (version.equals("1.6") || version.matches("^1\\.6\\D.*$")) {
181
182
183
184 Pattern datePattern = Pattern.compile(
185 "(?:^|[^0-9])(" +
186 "[2-9][0-9]{3}" +
187 "(?:0[1-9]|1[0-2])" +
188 "(?:0[1-9]|[12][0-9]|3[01])" +
189 ")(?:$|[^0-9])");
190
191 Matcher dateMatcher = datePattern.matcher(vminfo);
192 if (dateMatcher.find()) {
193 long dateValue = Long.parseLong(dateMatcher.group(1));
194 if (dateValue < 20081105L) {
195
196 return 2;
197 } else {
198
199 if (provider.equals("sun.nio.ch.EPollSelectorProvider")) {
200 return 0;
201 } else if (provider.equals("sun.nio.ch.PollSelectorProvider")) {
202 return 1;
203 }
204 }
205 }
206 }
207 }
208
209 } else if (vendor.contains("bea") || vendor.contains("oracle")) {
210
211 if (os.contains("linux")) {
212 if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
213 provider.equals("sun.nio.ch.PollSelectorProvider")) {
214 return 0;
215 }
216
217
218 } else if (os.contains("windows")) {
219 if (provider.equals("sun.nio.ch.WindowsSelectorProvider")) {
220 return 0;
221 }
222 }
223
224 } else if (vendor.contains("apache")) {
225 if (provider.equals("org.apache.harmony.nio.internal.SelectorProviderImpl")) {
226 return 1;
227 }
228 }
229
230
231 return -1;
232 }
233
234 private static int autodetect() {
235 final int constraintLevel;
236 ExecutorService executor = Executors.newCachedThreadPool();
237 boolean success;
238 long startTime;
239 int interestOps;
240
241 ServerSocketChannel ch = null;
242 SelectorLoop loop = null;
243
244 try {
245
246 ch = ServerSocketChannel.open();
247
248
249 try {
250 ch.socket().bind(new InetSocketAddress(0));
251 ch.configureBlocking(false);
252 } catch (Throwable e) {
253 if (logger.isWarnEnabled()) {
254 logger.warn("Failed to configure a temporary socket.", e);
255 }
256 return -1;
257 }
258
259
260 try {
261 loop = new SelectorLoop();
262 } catch (Throwable e) {
263 if (logger.isWarnEnabled()) {
264 logger.warn("Failed to open a temporary selector.", e);
265 }
266 return -1;
267 }
268
269
270 try {
271 ch.register(loop.selector, 0);
272 } catch (Throwable e) {
273 if (logger.isWarnEnabled()) {
274 logger.warn("Failed to register a temporary selector.", e);
275 }
276 return -1;
277 }
278
279 SelectionKey key = ch.keyFor(loop.selector);
280
281
282 executor.execute(loop);
283
284
285 success = true;
286 for (int i = 0; i < 10; i ++) {
287
288
289
290 do {
291 while (!loop.selecting) {
292 Thread.yield();
293 }
294
295
296 try {
297 Thread.sleep(50);
298 } catch (InterruptedException e) {
299
300 }
301 } while (!loop.selecting);
302
303 startTime = System.nanoTime();
304 key.interestOps(key.interestOps() | SelectionKey.OP_ACCEPT);
305 key.interestOps(key.interestOps() & ~SelectionKey.OP_ACCEPT);
306
307 if (System.nanoTime() - startTime >= 500000000L) {
308 success = false;
309 break;
310 }
311 }
312
313 if (success) {
314 constraintLevel = 0;
315 } else {
316
317 success = true;
318 for (int i = 0; i < 10; i ++) {
319
320
321
322 do {
323 while (!loop.selecting) {
324 Thread.yield();
325 }
326
327
328 try {
329 Thread.sleep(50);
330 } catch (InterruptedException e) {
331
332 }
333 } while (!loop.selecting);
334
335 startTime = System.nanoTime();
336 interestOps = key.interestOps();
337 synchronized (loop) {
338 loop.selector.wakeup();
339 key.interestOps(interestOps | SelectionKey.OP_ACCEPT);
340 key.interestOps(interestOps & ~SelectionKey.OP_ACCEPT);
341 }
342
343 if (System.nanoTime() - startTime >= 500000000L) {
344 success = false;
345 break;
346 }
347 }
348 if (success) {
349 constraintLevel = 1;
350 } else {
351 constraintLevel = 2;
352 }
353 }
354 } catch (Throwable e) {
355 return -1;
356 } finally {
357 if (ch != null) {
358 try {
359 ch.close();
360 } catch (Throwable e) {
361 if (logger.isWarnEnabled()) {
362 logger.warn("Failed to close a temporary socket.", e);
363 }
364 }
365 }
366
367 if (loop != null) {
368 loop.done = true;
369 try {
370 executor.shutdownNow();
371 } catch (NullPointerException ex) {
372
373 }
374
375 try {
376 for (;;) {
377 loop.selector.wakeup();
378 try {
379 if (executor.awaitTermination(1, TimeUnit.SECONDS)) {
380 break;
381 }
382 } catch (InterruptedException e) {
383
384 }
385 }
386 } catch (Throwable e) {
387
388 }
389
390 try {
391 loop.selector.close();
392 } catch (Throwable e) {
393 if (logger.isWarnEnabled()) {
394 logger.warn("Failed to close a temporary selector.", e);
395 }
396 }
397 }
398 }
399
400 return constraintLevel;
401 }
402
403 private static final class SelectorLoop implements Runnable {
404 final Selector selector;
405 volatile boolean done;
406 volatile boolean selecting;
407
408 SelectorLoop() throws IOException {
409 selector = Selector.open();
410 }
411
412 public void run() {
413 while (!done) {
414 synchronized (this) {
415
416 }
417 try {
418 selecting = true;
419 try {
420 selector.select(1000);
421 } finally {
422 selecting = false;
423 }
424
425 Set<SelectionKey> keys = selector.selectedKeys();
426 for (SelectionKey k: keys) {
427 k.interestOps(0);
428 }
429 keys.clear();
430 } catch (IOException e) {
431 if (logger.isWarnEnabled()) {
432 logger.warn("Failed to wait for a temporary selector.", e);
433 }
434 }
435 }
436 }
437 }
438
439 public static void main(String[] args) throws Exception {
440 for (Entry<Object, Object> e: System.getProperties().entrySet()) {
441 System.out.println(e.getKey() + ": " + e.getValue());
442 }
443 System.out.println();
444 System.out.println("Hard-coded Constraint Level: " + CONSTRAINT_LEVEL);
445 System.out.println("Auto-detected Constraint Level: " + autodetect());
446 }
447
448 private NioProviderMetadata() {
449
450 }
451 }