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