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    *   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.util.internal;
17  
18  import io.netty.util.CharsetUtil;
19  import io.netty.util.internal.chmv8.ConcurrentHashMapV8;
20  import io.netty.util.internal.logging.InternalLogger;
21  import io.netty.util.internal.logging.InternalLoggerFactory;
22  
23  import java.io.BufferedReader;
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStreamReader;
27  import java.lang.reflect.Field;
28  import java.lang.reflect.Method;
29  import java.net.InetSocketAddress;
30  import java.net.ServerSocket;
31  import java.nio.ByteBuffer;
32  import java.util.List;
33  import java.util.Locale;
34  import java.util.Map;
35  import java.util.Queue;
36  import java.util.concurrent.BlockingQueue;
37  import java.util.concurrent.ConcurrentHashMap;
38  import java.util.concurrent.ConcurrentMap;
39  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
40  import java.util.concurrent.atomic.AtomicLongFieldUpdater;
41  import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
42  import java.util.regex.Matcher;
43  import java.util.regex.Pattern;
44  
45  
46  /**
47   * Utility that detects various properties specific to the current runtime
48   * environment, such as Java version and the availability of the
49   * {@code sun.misc.Unsafe} object.
50   * <p>
51   * You can disable the use of {@code sun.misc.Unsafe} if you specify
52   * the system property <strong>io.netty.noUnsafe</strong>.
53   */
54  public final class PlatformDependent {
55  
56      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent.class);
57  
58      private static final Pattern MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN = Pattern.compile(
59              "\\s*-XX:MaxDirectMemorySize\\s*=\\s*([0-9]+)\\s*([kKmMgG]?)\\s*$");
60  
61      private static final boolean IS_ANDROID = isAndroid0();
62      private static final boolean IS_WINDOWS = isWindows0();
63      private static volatile Boolean IS_ROOT;
64  
65      private static final int JAVA_VERSION = javaVersion0();
66  
67      private static final boolean CAN_ENABLE_TCP_NODELAY_BY_DEFAULT = !isAndroid();
68  
69      private static final boolean HAS_UNSAFE = hasUnsafe0();
70      private static final boolean CAN_USE_CHM_V8 = HAS_UNSAFE && JAVA_VERSION < 8;
71      private static final boolean DIRECT_BUFFER_PREFERRED =
72              HAS_UNSAFE && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false);
73      private static final long MAX_DIRECT_MEMORY = maxDirectMemory0();
74  
75      private static final long ARRAY_BASE_OFFSET = arrayBaseOffset0();
76  
77      private static final boolean HAS_JAVASSIST = hasJavassist0();
78  
79      private static final File TMPDIR = tmpdir0();
80  
81      private static final int BIT_MODE = bitMode0();
82  
83      private static final int ADDRESS_SIZE = addressSize0();
84  
85      static {
86          if (logger.isDebugEnabled()) {
87              logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED);
88          }
89  
90          if (!hasUnsafe() && !isAndroid()) {
91              logger.info(
92                      "Your platform does not provide complete low-level API for accessing direct buffers reliably. " +
93                      "Unless explicitly requested, heap buffer will always be preferred to avoid potential system " +
94                      "unstability.");
95          }
96      }
97  
98      /**
99       * Returns {@code true} if and only if the current platform is Android
100      */
101     public static boolean isAndroid() {
102         return IS_ANDROID;
103     }
104 
105     /**
106      * Return {@code true} if the JVM is running on Windows
107      */
108     public static boolean isWindows() {
109         return IS_WINDOWS;
110     }
111 
112     /**
113      * Return {@code true} if the current user is root.  Note that this method returns
114      * {@code false} if on Windows.
115      */
116     public static boolean isRoot() {
117         if (IS_ROOT == null) {
118             synchronized (PlatformDependent.class) {
119                 if (IS_ROOT == null) {
120                     IS_ROOT = isRoot0();
121                 }
122             }
123         }
124         return IS_ROOT;
125     }
126 
127     /**
128      * Return the version of Java under which this library is used.
129      */
130     public static int javaVersion() {
131         return JAVA_VERSION;
132     }
133 
134     /**
135      * Returns {@code true} if and only if it is fine to enable TCP_NODELAY socket option by default.
136      */
137     public static boolean canEnableTcpNoDelayByDefault() {
138         return CAN_ENABLE_TCP_NODELAY_BY_DEFAULT;
139     }
140 
141     /**
142      * Return {@code true} if {@code sun.misc.Unsafe} was found on the classpath and can be used for acclerated
143      * direct memory access.
144      */
145     public static boolean hasUnsafe() {
146         return HAS_UNSAFE;
147     }
148 
149     /**
150      * Returns {@code true} if the platform has reliable low-level direct buffer access API and a user specified
151      * {@code -Dio.netty.preferDirect} option.
152      */
153     public static boolean directBufferPreferred() {
154         return DIRECT_BUFFER_PREFERRED;
155     }
156 
157     /**
158      * Returns the maximum memory reserved for direct buffer allocation.
159      */
160     public static long maxDirectMemory() {
161         return MAX_DIRECT_MEMORY;
162     }
163 
164     /**
165      * Returns {@code true} if and only if Javassist is available.
166      */
167     public static boolean hasJavassist() {
168         return HAS_JAVASSIST;
169     }
170 
171     /**
172      * Returns the temporary directory.
173      */
174     public static File tmpdir() {
175         return TMPDIR;
176     }
177 
178     /**
179      * Returns the bit mode of the current VM (usually 32 or 64.)
180      */
181     public static int bitMode() {
182         return BIT_MODE;
183     }
184 
185     /**
186      * Return the address size of the OS.
187      * 4 (for 32 bits systems ) and 8 (for 64 bits systems).
188      */
189     public static int addressSize() {
190         return ADDRESS_SIZE;
191     }
192 
193     public static long allocateMemory(long size) {
194         return PlatformDependent0.allocateMemory(size);
195     }
196 
197     public static void freeMemory(long address) {
198         PlatformDependent0.freeMemory(address);
199     }
200 
201     /**
202      * Raises an exception bypassing compiler checks for checked exceptions.
203      */
204     public static void throwException(Throwable t) {
205         if (hasUnsafe()) {
206             PlatformDependent0.throwException(t);
207         } else {
208             PlatformDependent.<RuntimeException>throwException0(t);
209         }
210     }
211 
212     @SuppressWarnings("unchecked")
213     private static <E extends Throwable> void throwException0(Throwable t) throws E {
214         throw (E) t;
215     }
216 
217     /**
218      * Creates a new fastest {@link ConcurrentMap} implementaion for the current platform.
219      */
220     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap() {
221         if (CAN_USE_CHM_V8) {
222             return new ConcurrentHashMapV8<K, V>();
223         } else {
224             return new ConcurrentHashMap<K, V>();
225         }
226     }
227 
228     /**
229      * Creates a new fastest {@link ConcurrentMap} implementaion for the current platform.
230      */
231     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(int initialCapacity) {
232         if (CAN_USE_CHM_V8) {
233             return new ConcurrentHashMapV8<K, V>(initialCapacity);
234         } else {
235             return new ConcurrentHashMap<K, V>(initialCapacity);
236         }
237     }
238 
239     /**
240      * Creates a new fastest {@link ConcurrentMap} implementaion for the current platform.
241      */
242     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(int initialCapacity, float loadFactor) {
243         if (CAN_USE_CHM_V8) {
244             return new ConcurrentHashMapV8<K, V>(initialCapacity, loadFactor);
245         } else {
246             return new ConcurrentHashMap<K, V>(initialCapacity, loadFactor);
247         }
248     }
249 
250     /**
251      * Creates a new fastest {@link ConcurrentMap} implementaion for the current platform.
252      */
253     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(
254             int initialCapacity, float loadFactor, int concurrencyLevel) {
255         if (CAN_USE_CHM_V8) {
256             return new ConcurrentHashMapV8<K, V>(initialCapacity, loadFactor, concurrencyLevel);
257         } else {
258             return new ConcurrentHashMap<K, V>(initialCapacity, loadFactor, concurrencyLevel);
259         }
260     }
261 
262     /**
263      * Creates a new fastest {@link ConcurrentMap} implementaion for the current platform.
264      */
265     public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap(Map<? extends K, ? extends V> map) {
266         if (CAN_USE_CHM_V8) {
267             return new ConcurrentHashMapV8<K, V>(map);
268         } else {
269             return new ConcurrentHashMap<K, V>(map);
270         }
271     }
272 
273     /**
274      * Try to deallocate the specified direct {@link ByteBuffer}.  Please note this method does nothing if
275      * the current platform does not support this operation or the specified buffer is not a direct buffer.
276      */
277     public static void freeDirectBuffer(ByteBuffer buffer) {
278         if (hasUnsafe() && !isAndroid()) {
279             // only direct to method if we are not running on android.
280             // See https://github.com/netty/netty/issues/2604
281             PlatformDependent0.freeDirectBuffer(buffer);
282         }
283     }
284 
285     public static long directBufferAddress(ByteBuffer buffer) {
286         return PlatformDependent0.directBufferAddress(buffer);
287     }
288 
289     public static Object getObject(Object object, long fieldOffset) {
290         return PlatformDependent0.getObject(object, fieldOffset);
291     }
292 
293     public static Object getObjectVolatile(Object object, long fieldOffset) {
294         return PlatformDependent0.getObjectVolatile(object, fieldOffset);
295     }
296 
297     public static int getInt(Object object, long fieldOffset) {
298         return PlatformDependent0.getInt(object, fieldOffset);
299     }
300 
301     public static long objectFieldOffset(Field field) {
302         return PlatformDependent0.objectFieldOffset(field);
303     }
304 
305     public static byte getByte(long address) {
306         return PlatformDependent0.getByte(address);
307     }
308 
309     public static short getShort(long address) {
310         return PlatformDependent0.getShort(address);
311     }
312 
313     public static int getInt(long address) {
314         return PlatformDependent0.getInt(address);
315     }
316 
317     public static long getLong(long address) {
318         return PlatformDependent0.getLong(address);
319     }
320 
321     public static void putOrderedObject(Object object, long address, Object value) {
322         PlatformDependent0.putOrderedObject(object, address, value);
323     }
324 
325     public static void putByte(long address, byte value) {
326         PlatformDependent0.putByte(address, value);
327     }
328 
329     public static void putShort(long address, short value) {
330         PlatformDependent0.putShort(address, value);
331     }
332 
333     public static void putInt(long address, int value) {
334         PlatformDependent0.putInt(address, value);
335     }
336 
337     public static void putLong(long address, long value) {
338         PlatformDependent0.putLong(address, value);
339     }
340 
341     public static void copyMemory(long srcAddr, long dstAddr, long length) {
342         PlatformDependent0.copyMemory(srcAddr, dstAddr, length);
343     }
344 
345     public static void copyMemory(byte[] src, int srcIndex, long dstAddr, long length) {
346         PlatformDependent0.copyMemory(src, ARRAY_BASE_OFFSET + srcIndex, null, dstAddr, length);
347     }
348 
349     public static void copyMemory(long srcAddr, byte[] dst, int dstIndex, long length) {
350         PlatformDependent0.copyMemory(null, srcAddr, dst, ARRAY_BASE_OFFSET + dstIndex, length);
351     }
352 
353     /**
354      * Create a new optimized {@link AtomicReferenceFieldUpdater} or {@code null} if it
355      * could not be created. Because of this the caller need to check for {@code null} and if {@code null} is returned
356      * use {@link AtomicReferenceFieldUpdater#newUpdater(Class, Class, String)} as fallback.
357      */
358     public static <U, W> AtomicReferenceFieldUpdater<U, W> newAtomicReferenceFieldUpdater(
359             Class<U> tclass, String fieldName) {
360         if (hasUnsafe()) {
361             try {
362                 return PlatformDependent0.newAtomicReferenceFieldUpdater(tclass, fieldName);
363             } catch (Throwable ignore) {
364                 // ignore
365             }
366         }
367         return null;
368     }
369 
370     /**
371      * Create a new optimized {@link AtomicIntegerFieldUpdater} or {@code null} if it
372      * could not be created. Because of this the caller need to check for {@code null} and if {@code null} is returned
373      * use {@link AtomicIntegerFieldUpdater#newUpdater(Class, String)} as fallback.
374      */
375     public static <T> AtomicIntegerFieldUpdater<T> newAtomicIntegerFieldUpdater(
376             Class<?> tclass, String fieldName) {
377         if (hasUnsafe()) {
378             try {
379                 return PlatformDependent0.newAtomicIntegerFieldUpdater(tclass, fieldName);
380             } catch (Throwable ignore) {
381                 // ignore
382             }
383         }
384         return null;
385     }
386 
387     /**
388      * Create a new optimized {@link AtomicLongFieldUpdater} or {@code null} if it
389      * could not be created. Because of this the caller need to check for {@code null} and if {@code null} is returned
390      * use {@link AtomicLongFieldUpdater#newUpdater(Class, String)} as fallback.
391      */
392     public static <T> AtomicLongFieldUpdater<T> newAtomicLongFieldUpdater(
393             Class<?> tclass, String fieldName) {
394         if (hasUnsafe()) {
395             try {
396                 return PlatformDependent0.newAtomicLongFieldUpdater(tclass, fieldName);
397             } catch (Throwable ignore) {
398                 // ignore
399             }
400         }
401         return null;
402     }
403 
404     /**
405      * Create a new {@link Queue} which is safe to use for multiple producers (different threads) and a single
406      * consumer (one thread!).
407      */
408     public static <T> Queue<T> newMpscQueue() {
409         return new MpscLinkedQueue<T>();
410     }
411 
412     /**
413      * Return the {@link ClassLoader} for the given {@link Class}.
414      */
415     public static ClassLoader getClassLoader(final Class<?> clazz) {
416         return PlatformDependent0.getClassLoader(clazz);
417     }
418 
419     /**
420      * Return the context {@link ClassLoader} for the current {@link Thread}.
421      */
422     public static ClassLoader getContextClassLoader() {
423         return PlatformDependent0.getContextClassLoader();
424     }
425 
426     /**
427      * Return the system {@link ClassLoader}.
428      */
429     public static ClassLoader getSystemClassLoader() {
430         return PlatformDependent0.getSystemClassLoader();
431     }
432 
433     private static boolean isAndroid0() {
434         boolean android;
435         try {
436             Class.forName("android.app.Application", false, getSystemClassLoader());
437             android = true;
438         } catch (Exception e) {
439             // Failed to load the class uniquely available in Android.
440             android = false;
441         }
442 
443         if (android) {
444             logger.debug("Platform: Android");
445         }
446         return android;
447     }
448 
449     private static boolean isWindows0() {
450         boolean windows = SystemPropertyUtil.get("os.name", "").toLowerCase(Locale.US).contains("win");
451         if (windows) {
452             logger.debug("Platform: Windows");
453         }
454         return windows;
455     }
456 
457     private static boolean isRoot0() {
458         if (isWindows()) {
459             return false;
460         }
461 
462         String[] ID_COMMANDS = { "/usr/bin/id", "/bin/id", "/usr/xpg4/bin/id", "id"};
463         Pattern UID_PATTERN = Pattern.compile("^(?:0|[1-9][0-9]*)$");
464         for (String idCmd: ID_COMMANDS) {
465             Process p = null;
466             BufferedReader in = null;
467             String uid = null;
468             try {
469                 p = Runtime.getRuntime().exec(new String[] { idCmd, "-u" });
470                 in = new BufferedReader(new InputStreamReader(p.getInputStream(), CharsetUtil.US_ASCII));
471                 uid = in.readLine();
472                 in.close();
473 
474                 for (;;) {
475                     try {
476                         int exitCode = p.waitFor();
477                         if (exitCode != 0) {
478                             uid = null;
479                         }
480                         break;
481                     } catch (InterruptedException e) {
482                         // Ignore
483                     }
484                 }
485             } catch (Exception e) {
486                 // Failed to run the command.
487                 uid = null;
488             } finally {
489                 if (in != null) {
490                     try {
491                         in.close();
492                     } catch (IOException e) {
493                         // Ignore
494                     }
495                 }
496                 if (p != null) {
497                     try {
498                         p.destroy();
499                     } catch (Exception e) {
500                         // Android sometimes triggers an ErrnoException.
501                     }
502                 }
503             }
504 
505             if (uid != null && UID_PATTERN.matcher(uid).matches()) {
506                 logger.debug("UID: {}", uid);
507                 return "0".equals(uid);
508             }
509         }
510 
511         logger.debug("Could not determine the current UID using /usr/bin/id; attempting to bind at privileged ports.");
512 
513         Pattern PERMISSION_DENIED = Pattern.compile(".*(?:denied|not.*permitted).*");
514         for (int i = 1023; i > 0; i --) {
515             ServerSocket ss = null;
516             try {
517                 ss = new ServerSocket();
518                 ss.setReuseAddress(true);
519                 ss.bind(new InetSocketAddress(i));
520                 if (logger.isDebugEnabled()) {
521                     logger.debug("UID: 0 (succeded to bind at port {})", i);
522                 }
523                 return true;
524             } catch (Exception e) {
525                 // Failed to bind.
526                 // Check the error message so that we don't always need to bind 1023 times.
527                 String message = e.getMessage();
528                 if (message == null) {
529                     message = "";
530                 }
531                 message = message.toLowerCase();
532                 if (PERMISSION_DENIED.matcher(message).matches()) {
533                     break;
534                 }
535             } finally {
536                 if (ss != null) {
537                     try {
538                         ss.close();
539                     } catch (Exception e) {
540                         // Ignore.
541                     }
542                 }
543             }
544         }
545 
546         logger.debug("UID: non-root (failed to bind at any privileged ports)");
547         return false;
548     }
549 
550     @SuppressWarnings("LoopStatementThatDoesntLoop")
551     private static int javaVersion0() {
552         int javaVersion;
553 
554         // Not really a loop
555         for (;;) {
556             // Android
557             if (isAndroid()) {
558                 javaVersion = 6;
559                 break;
560             }
561 
562             try {
563                 Class.forName("java.time.Clock", false, getClassLoader(Object.class));
564                 javaVersion = 8;
565                 break;
566             } catch (Exception e) {
567                 // Ignore
568             }
569 
570             try {
571                 Class.forName("java.util.concurrent.LinkedTransferQueue", false, getClassLoader(BlockingQueue.class));
572                 javaVersion = 7;
573                 break;
574             } catch (Exception e) {
575                 // Ignore
576             }
577 
578             javaVersion = 6;
579             break;
580         }
581 
582         if (logger.isDebugEnabled()) {
583             logger.debug("Java version: {}", javaVersion);
584         }
585         return javaVersion;
586     }
587 
588     private static boolean hasUnsafe0() {
589         boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
590         logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
591 
592         if (isAndroid()) {
593             logger.debug("sun.misc.Unsafe: unavailable (Android)");
594             return false;
595         }
596 
597         if (noUnsafe) {
598             logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
599             return false;
600         }
601 
602         // Legacy properties
603         boolean tryUnsafe;
604         if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
605             tryUnsafe = SystemPropertyUtil.getBoolean("io.netty.tryUnsafe", true);
606         } else {
607             tryUnsafe = SystemPropertyUtil.getBoolean("org.jboss.netty.tryUnsafe", true);
608         }
609 
610         if (!tryUnsafe) {
611             logger.debug("sun.misc.Unsafe: unavailable (io.netty.tryUnsafe/org.jboss.netty.tryUnsafe)");
612             return false;
613         }
614 
615         try {
616             boolean hasUnsafe = PlatformDependent0.hasUnsafe();
617             logger.debug("sun.misc.Unsafe: {}", hasUnsafe ? "available" : "unavailable");
618             return hasUnsafe;
619         } catch (Throwable t) {
620             // Probably failed to initialize PlatformDependent0.
621             return false;
622         }
623     }
624 
625     private static long arrayBaseOffset0() {
626         if (!hasUnsafe()) {
627             return -1;
628         }
629 
630         return PlatformDependent0.arrayBaseOffset();
631     }
632 
633     private static long maxDirectMemory0() {
634         long maxDirectMemory = 0;
635         try {
636             // Try to get from sun.misc.VM.maxDirectMemory() which should be most accurate.
637             Class<?> vmClass = Class.forName("sun.misc.VM", true, getSystemClassLoader());
638             Method m = vmClass.getDeclaredMethod("maxDirectMemory");
639             maxDirectMemory = ((Number) m.invoke(null)).longValue();
640         } catch (Throwable t) {
641             // Ignore
642         }
643 
644         if (maxDirectMemory > 0) {
645             return maxDirectMemory;
646         }
647 
648         try {
649             // Now try to get the JVM option (-XX:MaxDirectMemorySize) and parse it.
650             // Note that we are using reflection because Android doesn't have these classes.
651             Class<?> mgmtFactoryClass = Class.forName(
652                     "java.lang.management.ManagementFactory", true, getSystemClassLoader());
653             Class<?> runtimeClass = Class.forName(
654                     "java.lang.management.RuntimeMXBean", true, getSystemClassLoader());
655 
656             Object runtime = mgmtFactoryClass.getDeclaredMethod("getRuntimeMXBean").invoke(null);
657 
658             @SuppressWarnings("unchecked")
659             List<String> vmArgs = (List<String>) runtimeClass.getDeclaredMethod("getInputArguments").invoke(runtime);
660             for (int i = vmArgs.size() - 1; i >= 0; i --) {
661                 Matcher m = MAX_DIRECT_MEMORY_SIZE_ARG_PATTERN.matcher(vmArgs.get(i));
662                 if (!m.matches()) {
663                     continue;
664                 }
665 
666                 maxDirectMemory = Long.parseLong(m.group(1));
667                 switch (m.group(2).charAt(0)) {
668                     case 'k': case 'K':
669                         maxDirectMemory *= 1024;
670                         break;
671                     case 'm': case 'M':
672                         maxDirectMemory *= 1024 * 1024;
673                         break;
674                     case 'g': case 'G':
675                         maxDirectMemory *= 1024 * 1024 * 1024;
676                         break;
677                 }
678                 break;
679             }
680         } catch (Throwable t) {
681             // Ignore
682         }
683 
684         if (maxDirectMemory <= 0) {
685             maxDirectMemory = Runtime.getRuntime().maxMemory();
686             logger.debug("maxDirectMemory: {} bytes (maybe)", maxDirectMemory);
687         } else {
688             logger.debug("maxDirectMemory: {} bytes", maxDirectMemory);
689         }
690 
691         return maxDirectMemory;
692     }
693 
694     private static boolean hasJavassist0() {
695         if (isAndroid()) {
696             return false;
697         }
698 
699         boolean noJavassist = SystemPropertyUtil.getBoolean("io.netty.noJavassist", false);
700         logger.debug("-Dio.netty.noJavassist: {}", noJavassist);
701 
702         if (noJavassist) {
703             logger.debug("Javassist: unavailable (io.netty.noJavassist)");
704             return false;
705         }
706 
707         try {
708             JavassistTypeParameterMatcherGenerator.generate(Object.class, getClassLoader(PlatformDependent.class));
709             logger.debug("Javassist: available");
710             return true;
711         } catch (Throwable t) {
712             // Failed to generate a Javassist-based matcher.
713             logger.debug("Javassist: unavailable");
714             logger.debug(
715                     "You don't have Javassist in your class path or you don't have enough permission " +
716                     "to load dynamically generated classes.  Please check the configuration for better performance.");
717             return false;
718         }
719     }
720 
721     private static File tmpdir0() {
722         File f;
723         try {
724             f = toDirectory(SystemPropertyUtil.get("io.netty.tmpdir"));
725             if (f != null) {
726                 logger.debug("-Dio.netty.tmpdir: {}", f);
727                 return f;
728             }
729 
730             f = toDirectory(SystemPropertyUtil.get("java.io.tmpdir"));
731             if (f != null) {
732                 logger.debug("-Dio.netty.tmpdir: {} (java.io.tmpdir)", f);
733                 return f;
734             }
735 
736             // This shouldn't happen, but just in case ..
737             if (isWindows()) {
738                 f = toDirectory(System.getenv("TEMP"));
739                 if (f != null) {
740                     logger.debug("-Dio.netty.tmpdir: {} (%TEMP%)", f);
741                     return f;
742                 }
743 
744                 String userprofile = System.getenv("USERPROFILE");
745                 if (userprofile != null) {
746                     f = toDirectory(userprofile + "\\AppData\\Local\\Temp");
747                     if (f != null) {
748                         logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\AppData\\Local\\Temp)", f);
749                         return f;
750                     }
751 
752                     f = toDirectory(userprofile + "\\Local Settings\\Temp");
753                     if (f != null) {
754                         logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\Local Settings\\Temp)", f);
755                         return f;
756                     }
757                 }
758             } else {
759                 f = toDirectory(System.getenv("TMPDIR"));
760                 if (f != null) {
761                     logger.debug("-Dio.netty.tmpdir: {} ($TMPDIR)", f);
762                     return f;
763                 }
764             }
765         } catch (Exception ignored) {
766             // Environment variable inaccessible
767         }
768 
769         // Last resort.
770         if (isWindows()) {
771             f = new File("C:\\Windows\\Temp");
772         } else {
773             f = new File("/tmp");
774         }
775 
776         logger.warn("Failed to get the temporary directory; falling back to: {}", f);
777         return f;
778     }
779 
780     @SuppressWarnings("ResultOfMethodCallIgnored")
781     private static File toDirectory(String path) {
782         if (path == null) {
783             return null;
784         }
785 
786         File f = new File(path);
787         f.mkdirs();
788 
789         if (!f.isDirectory()) {
790             return null;
791         }
792 
793         try {
794             return f.getAbsoluteFile();
795         } catch (Exception ignored) {
796             return f;
797         }
798     }
799 
800     private static int bitMode0() {
801         // Check user-specified bit mode first.
802         int bitMode = SystemPropertyUtil.getInt("io.netty.bitMode", 0);
803         if (bitMode > 0) {
804             logger.debug("-Dio.netty.bitMode: {}", bitMode);
805             return bitMode;
806         }
807 
808         // And then the vendor specific ones which is probably most reliable.
809         bitMode = SystemPropertyUtil.getInt("sun.arch.data.model", 0);
810         if (bitMode > 0) {
811             logger.debug("-Dio.netty.bitMode: {} (sun.arch.data.model)", bitMode);
812             return bitMode;
813         }
814         bitMode = SystemPropertyUtil.getInt("com.ibm.vm.bitmode", 0);
815         if (bitMode > 0) {
816             logger.debug("-Dio.netty.bitMode: {} (com.ibm.vm.bitmode)", bitMode);
817             return bitMode;
818         }
819 
820         // os.arch also gives us a good hint.
821         String arch = SystemPropertyUtil.get("os.arch", "").toLowerCase(Locale.US).trim();
822         if ("amd64".equals(arch) || "x86_64".equals(arch)) {
823             bitMode = 64;
824         } else if ("i386".equals(arch) || "i486".equals(arch) || "i586".equals(arch) || "i686".equals(arch)) {
825             bitMode = 32;
826         }
827 
828         if (bitMode > 0) {
829             logger.debug("-Dio.netty.bitMode: {} (os.arch: {})", bitMode, arch);
830         }
831 
832         // Last resort: guess from VM name and then fall back to most common 64-bit mode.
833         String vm = SystemPropertyUtil.get("java.vm.name", "").toLowerCase(Locale.US);
834         Pattern BIT_PATTERN = Pattern.compile("([1-9][0-9]+)-?bit");
835         Matcher m = BIT_PATTERN.matcher(vm);
836         if (m.find()) {
837             return Integer.parseInt(m.group(1));
838         } else {
839             return 64;
840         }
841     }
842 
843     private static int addressSize0() {
844         if (!hasUnsafe()) {
845             return -1;
846         }
847         return PlatformDependent0.addressSize();
848     }
849 
850     private PlatformDependent() {
851         // only static method supported
852     }
853 }