View Javadoc
1   /*
2    * Copyright 2014 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  
17  package org.jboss.netty.util.internal;
18  
19  import org.jboss.netty.logging.InternalLogger;
20  import org.jboss.netty.logging.InternalLoggerFactory;
21  
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.net.URL;
28  import java.util.Locale;
29  
30  /**
31   * Helper class to load JNI resources.
32   *
33   */
34  public final class NativeLibraryLoader {
35  
36      private static final InternalLogger logger = InternalLoggerFactory.getInstance(NativeLibraryLoader.class);
37  
38      private static final String NATIVE_RESOURCE_HOME = "META-INF/native/";
39      private static final String OSNAME;
40      private static final File WORKDIR;
41  
42      static {
43          OSNAME = SystemPropertyUtil.get("os.name", "").toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
44  
45          String workdir = SystemPropertyUtil.get("io.netty.native.workdir");
46          if (workdir != null) {
47              File f = new File(workdir);
48              if (!f.exists()) {
49                  // ok to ignore as createTempFile will take care
50                  //noinspection ResultOfMethodCallIgnored
51                  f.mkdirs();
52              }
53  
54              try {
55                  f = f.getAbsoluteFile();
56              } catch (Exception ignored) {
57                  // Good to have an absolute path, but it's OK.
58              }
59  
60              WORKDIR = f;
61              logger.debug("-Dio.netty.netty.workdir: " + WORKDIR);
62          } else {
63              WORKDIR = tmpdir();
64              logger.debug("-Dio.netty.netty.workdir: " + WORKDIR + " (io.netty.tmpdir)");
65          }
66      }
67  
68      private static File tmpdir() {
69          File f;
70          try {
71              f = toDirectory(SystemPropertyUtil.get("io.netty.tmpdir"));
72              if (f != null) {
73                  logger.debug("-Dio.netty.tmpdir: " + f);
74                  return f;
75              }
76  
77              f = toDirectory(SystemPropertyUtil.get("java.io.tmpdir"));
78              if (f != null) {
79                  logger.debug("-Dio.netty.tmpdir: " + f + " (java.io.tmpdir)");
80                  return f;
81              }
82  
83              // This shouldn't happen, but just in case ..
84              if (isWindows()) {
85                  f = toDirectory(System.getenv("TEMP"));
86                  if (f != null) {
87                      logger.debug("-Dio.netty.tmpdir: " + f + " (%TEMP%)");
88                      return f;
89                  }
90  
91                  String userprofile = System.getenv("USERPROFILE");
92                  if (userprofile != null) {
93                      f = toDirectory(userprofile + "\\AppData\\Local\\Temp");
94                      if (f != null) {
95                          logger.debug("-Dio.netty.tmpdir: " + f + " (%USERPROFILE%\\AppData\\Local\\Temp)");
96                          return f;
97                      }
98  
99                      f = toDirectory(userprofile + "\\Local Settings\\Temp");
100                     if (f != null) {
101                         logger.debug("-Dio.netty.tmpdir: " + f + " (%USERPROFILE%\\Local Settings\\Temp)");
102                         return f;
103                     }
104                 }
105             } else {
106                 f = toDirectory(System.getenv("TMPDIR"));
107                 if (f != null) {
108                     logger.debug("-Dio.netty.tmpdir: " + f + " ($TMPDIR)");
109                     return f;
110                 }
111             }
112         } catch (Exception ignored) {
113             // Environment variable inaccessible
114         }
115 
116         // Last resort.
117         if (isWindows()) {
118             f = new File("C:\\Windows\\Temp");
119         } else {
120             f = new File("/tmp");
121         }
122 
123         logger.warn("Failed to get the temporary directory; falling back to: " + f);
124         return f;
125     }
126 
127     @SuppressWarnings("ResultOfMethodCallIgnored")
128     private static File toDirectory(String path) {
129         if (path == null) {
130             return null;
131         }
132 
133         File f = new File(path);
134         if (!f.exists()) {
135             f.mkdirs();
136         }
137 
138         if (!f.isDirectory()) {
139             return null;
140         }
141 
142         try {
143             return f.getAbsoluteFile();
144         } catch (Exception ignored) {
145             return f;
146         }
147     }
148 
149     private static boolean isWindows() {
150         return OSNAME.startsWith("windows");
151     }
152 
153     private static boolean isOSX() {
154         return OSNAME.startsWith("macosx") || OSNAME.startsWith("osx");
155     }
156 
157     /**
158      * Load the given library with the specified {@link java.lang.ClassLoader}
159      */
160     public static void load(String name, ClassLoader loader) {
161         String libname = System.mapLibraryName(name);
162         String path = NATIVE_RESOURCE_HOME + libname;
163 
164         URL url = loader.getResource(path);
165         if (url == null && isOSX()) {
166             if (path.endsWith(".jnilib")) {
167                 url = loader.getResource(NATIVE_RESOURCE_HOME + "lib" + name + ".dynlib");
168             } else {
169                 url = loader.getResource(NATIVE_RESOURCE_HOME + "lib" + name + ".jnilib");
170             }
171         }
172 
173         if (url == null) {
174             // Fall back to normal loading of JNI stuff
175             System.loadLibrary(name);
176             return;
177         }
178 
179         int index = libname.lastIndexOf('.');
180         String prefix = libname.substring(0, index);
181         String suffix = libname.substring(index, libname.length());
182         InputStream in = null;
183         OutputStream out = null;
184         File tmpFile = null;
185         boolean loaded = false;
186         try {
187             tmpFile = File.createTempFile(prefix, suffix, WORKDIR);
188             in = url.openStream();
189             out = new FileOutputStream(tmpFile);
190 
191             byte[] buffer = new byte[8192];
192             int length;
193             while ((length = in.read(buffer)) > 0) {
194                 out.write(buffer, 0, length);
195             }
196             out.flush();
197             out.close();
198             out = null;
199 
200             System.load(tmpFile.getPath());
201             loaded = true;
202         } catch (Exception e) {
203             throw (UnsatisfiedLinkError) new UnsatisfiedLinkError(
204                     "could not load a native library: " + name).initCause(e);
205         } finally {
206             if (in != null) {
207                 try {
208                     in.close();
209                 } catch (IOException ignore) {
210                     // ignore
211                 }
212             }
213             if (out != null) {
214                 try {
215                     out.close();
216                 } catch (IOException ignore) {
217                     // ignore
218                 }
219             }
220             if (tmpFile != null) {
221                 if (loaded) {
222                     tmpFile.deleteOnExit();
223                 } else {
224                     if (!tmpFile.delete()) {
225                         tmpFile.deleteOnExit();
226                     }
227                 }
228             }
229         }
230     }
231 
232     private NativeLibraryLoader() {
233         // Utility
234     }
235 }