GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
TempJarCache.java
Go to the documentation of this file.
1/**
2 * Copyright 2011 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.common.util.cache;
29
30import java.io.File;
31import java.io.IOException;
32import java.net.URISyntaxException;
33import java.security.cert.Certificate;
34import java.util.HashMap;
35import java.util.Map;
36import java.util.jar.JarFile;
37
38import jogamp.common.Debug;
39
40import com.jogamp.common.JogampRuntimeException;
41import com.jogamp.common.net.Uri;
42import com.jogamp.common.os.NativeLibrary;
43import com.jogamp.common.util.JarUtil;
44import com.jogamp.common.util.SecurityUtil;
45
46/**
47 * Static Jar file cache handler using an underlying instance of {@link TempFileCache}, see {@link #getTempFileCache()}.
48 * <p>
49 * Lifecycle: Concurrently running JVMs and ClassLoader
50 * </p>
51 */
52public class TempJarCache {
53 private static final boolean DEBUG = Debug.debug("TempJarCache");
54
55 // A HashMap of native libraries that can be loaded with System.load()
56 // The key is the string name of the library as passed into the loadLibrary
57 // call; it is the file name without the directory or the platform-dependent
58 // library prefix and suffix. The value is the absolute path name to the
59 // unpacked library file in nativeTmpDir.
60 private static Map<String, String> nativeLibMap;
61
62 public enum LoadState {
63 LOOKED_UP, LOADED;
64
65 public boolean compliesWith(final LoadState o2) {
66 return null != o2 ? compareTo(o2) >= 0 : false;
67 }
68 }
69 private static boolean testLoadState(final LoadState has, final LoadState exp) {
70 if(null == has) {
71 return null == exp;
72 }
73 return has.compliesWith(exp);
74 }
75
76 // Set of jar files added
77 private static Map<Uri, LoadState> nativeLibJars;
78 private static Map<Uri, LoadState> classFileJars;
79 private static Map<Uri, LoadState> resourceFileJars;
80
81 private static TempFileCache tmpFileCache;
82
83 private static volatile boolean staticInitError = false;
84 private static volatile boolean staticTempIsExecutable = true;
85 private static volatile boolean isInit = false;
86
87 /**
88 * Documented way to kick off static initialization.
89 *
90 * @return true is static initialization was successful
91 */
92 public static boolean initSingleton() {
93 if (!isInit) { // volatile: ok
94 synchronized (TempJarCache.class) {
95 if (!isInit) {
96 staticInitError = !TempFileCache.initSingleton();
97
98 if(!staticInitError) {
99 tmpFileCache = new TempFileCache();
100 staticInitError = !tmpFileCache.isValid(false);
101 staticTempIsExecutable = tmpFileCache.isValid(true);
102 }
103
104 if(!staticInitError) {
105 // Initialize the collections of resources
106 nativeLibMap = new HashMap<String, String>();
107 nativeLibJars = new HashMap<Uri, LoadState>();
108 classFileJars = new HashMap<Uri, LoadState>();
109 resourceFileJars = new HashMap<Uri, LoadState>();
110 }
111 if(DEBUG) {
112 final File tempDir = null != tmpFileCache ? tmpFileCache.getTempDir() : null;
113 final String tempDirAbsPath = null != tempDir ? tempDir.getAbsolutePath() : null;
114 System.err.println("TempJarCache.initSingleton(): ok "+(false==staticInitError)+", "+ tempDirAbsPath+", executable "+staticTempIsExecutable);
115 }
116 isInit = true;
117 }
118 }
119 }
120 return !staticInitError;
121 }
122
123 /**
124 * This is <b>not recommended</b> since the JNI libraries may still be
125 * in use by the ClassLoader they are loaded via {@link System#load(String)}.
126 * </p>
127 * <p>
128 * In JogAmp, JNI native libraries loaded and registered by {@link JNILibLoaderBase}
129 * derivations, where the native JARs might be loaded via {@link JNILibLoaderBase#addNativeJarLibs(Class, String) }.
130 * </p>
131 * <p>
132 * The only valid use case to shutdown the TempJarCache is at bootstrapping,
133 * i.e. when no native library is guaranteed to be loaded. This could be useful
134 * if bootstrapping needs to find the proper native library type.
135 * </p>
136 *
137 public static void shutdown() {
138 if (isInit) { // volatile: ok
139 synchronized (TempJarCache.class) {
140 if (isInit) {
141 if(DEBUG) {
142 System.err.println("TempJarCache.shutdown(): real "+(false==staticInitError)+", "+ tmpFileCache.getTempDir());
143 }
144 isInit = false;
145 if(!staticInitError) {
146 nativeLibMap.clear();
147 nativeLibMap = null;
148 nativeLibJars.clear();
149 nativeLibJars = null;
150 classFileJars.clear();
151 classFileJars = null;
152 resourceFileJars.clear();
153 resourceFileJars = null;
154
155 tmpFileCache.destroy();
156 tmpFileCache = null;
157 }
158 }
159 }
160 }
161 } */
162
163 private static boolean isInitializedImpl() {
164 if (!isInit) { // volatile: ok
165 synchronized (TempJarCache.class) {
166 if (!isInit) {
167 return false;
168 }
169 }
170 }
171 return true;
172 }
173
174 /**
175 * @param forExecutables if {@code true}, method also tests whether the underlying cache is suitable to load native libraries or launch executables
176 * @return true if this class has been properly initialized, ie. is in use. Otherwise returns false.
177 */
178 public static boolean isInitialized(final boolean forExecutables) {
179 return isInitializedImpl() && !staticInitError && ( !forExecutables || staticTempIsExecutable );
180 }
181
182 /**
183 * @param forExecutables if {@code true}, method also tests whether the underlying cache is suitable to load native libraries or launch executables
184 */
185 /* package */ static void checkInitialized(final boolean forExecutables) {
186 if(!isInitializedImpl()) {
187 throw new JogampRuntimeException("initSingleton() has to be called first.");
188 }
189 if(staticInitError) {
190 throw new JogampRuntimeException("initSingleton() failed.");
191 }
192 if( forExecutables && !staticTempIsExecutable ) {
193 throw new JogampRuntimeException("TempJarCache folder not suitable for executables");
194 }
195 }
196
197 /**
198 * @return the underlying {@link TempFileCache}
199 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
200 */
202 checkInitialized(false);
203 return tmpFileCache;
204 }
205
206 /**
207 * @param jarUri
208 * @param exp
209 * @return
210 * @throws IOException
211 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
212 */
213 public synchronized static boolean checkNativeLibs(final Uri jarUri, final LoadState exp) throws IOException {
214 checkInitialized(false);
215 if(null == jarUri) {
216 throw new IllegalArgumentException("jarUri is null");
217 }
218 return testLoadState(nativeLibJars.get(jarUri), exp);
219 }
220
221 /**
222 * @param jarUri
223 * @param exp
224 * @return
225 * @throws IOException
226 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
227 */
228 public synchronized static boolean checkClasses(final Uri jarUri, final LoadState exp) throws IOException {
229 checkInitialized(false);
230 if(null == jarUri) {
231 throw new IllegalArgumentException("jarUri is null");
232 }
233 return testLoadState(classFileJars.get(jarUri), exp);
234 }
235
236 /**
237 *
238 * @param jarUri
239 * @param exp
240 * @return
241 * @throws IOException
242 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
243 */
244 public synchronized static boolean checkResources(final Uri jarUri, final LoadState exp) throws IOException {
245 checkInitialized(false);
246 if(null == jarUri) {
247 throw new IllegalArgumentException("jarUri is null");
248 }
249 return testLoadState(resourceFileJars.get(jarUri), exp);
250 }
251
252 /**
253 * Adds native libraries, if not yet added.
254 *
255 * @param certClass if class is certified, the JarFile entries needs to have the same certificate
256 * @param jarUri
257 * @param nativeLibraryPath if not null, only extracts native libraries within this path.
258 * @return true if native libraries were added or previously loaded from given jarUri, otherwise false
259 * @throws IOException if the <code>jarUri</code> could not be loaded or a previous load attempt failed
260 * @throws SecurityException
261 * @throws URISyntaxException
262 * @throws IllegalArgumentException
263 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(true)}
264 */
265 public synchronized static final boolean addNativeLibs(final Class<?> certClass, final Uri jarUri, final String nativeLibraryPath) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
266 checkInitialized(true);
267 final LoadState nativeLibJarsLS = nativeLibJars.get(jarUri);
268 if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) ) {
269 nativeLibJars.put(jarUri, LoadState.LOOKED_UP);
270 final JarFile jarFile = JarUtil.getJarFile(jarUri);
271 if(DEBUG) {
272 System.err.println("TempJarCache: addNativeLibs: "+jarUri+": nativeJar "+jarFile.getName()+" (NEW)");
273 }
274 validateCertificates(certClass, jarFile);
275 final int num = JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, nativeLibraryPath, true, false, false);
276 nativeLibJars.put(jarUri, LoadState.LOADED);
277 return num > 0;
278 } else if( testLoadState(nativeLibJarsLS, LoadState.LOADED) ) {
279 if(DEBUG) {
280 System.err.println("TempJarCache: addNativeLibs: "+jarUri+": nativeJar "+jarUri+" (REUSE)");
281 }
282 return true;
283 }
284 throw new IOException("TempJarCache: addNativeLibs: "+jarUri+", previous load attempt failed");
285 }
286
287 /**
288 * Adds native classes, if not yet added.
289 *
290 * TODO class access pending
291 * needs Classloader.defineClass(..) access, ie. own derivation - will do when needed ..
292 *
293 * @param certClass if class is certified, the JarFile entries needs to have the same certificate
294 * @param jarUri
295 * @throws IOException if the <code>jarUri</code> could not be loaded or a previous load attempt failed
296 * @throws SecurityException
297 * @throws URISyntaxException
298 * @throws IllegalArgumentException
299 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
300 */
301 public synchronized static final void addClasses(final Class<?> certClass, final Uri jarUri) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
302 checkInitialized(false);
303 final LoadState classFileJarsLS = classFileJars.get(jarUri);
304 if( !testLoadState(classFileJarsLS, LoadState.LOOKED_UP) ) {
305 classFileJars.put(jarUri, LoadState.LOOKED_UP);
306 final JarFile jarFile = JarUtil.getJarFile(jarUri);
307 if(DEBUG) {
308 System.err.println("TempJarCache: addClasses: "+jarUri+": nativeJar "+jarFile.getName());
309 }
310 validateCertificates(certClass, jarFile);
311 JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile,
312 null /* nativeLibraryPath */, false, true, false);
313 classFileJars.put(jarUri, LoadState.LOADED);
314 } else if( !testLoadState(classFileJarsLS, LoadState.LOADED) ) {
315 throw new IOException("TempJarCache: addClasses: "+jarUri+", previous load attempt failed");
316 }
317 }
318
319 /**
320 * Adds native resources, if not yet added.
321 *
322 * @param certClass if class is certified, the JarFile entries needs to have the same certificate
323 * @param jarUri
324 * @return
325 * @throws IOException if the <code>jarUri</code> could not be loaded or a previous load attempt failed
326 * @throws SecurityException
327 * @throws URISyntaxException
328 * @throws IllegalArgumentException
329 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
330 */
331 public synchronized static final void addResources(final Class<?> certClass, final Uri jarUri) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
332 checkInitialized(false);
333 final LoadState resourceFileJarsLS = resourceFileJars.get(jarUri);
334 if( !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP) ) {
335 resourceFileJars.put(jarUri, LoadState.LOOKED_UP);
336 final JarFile jarFile = JarUtil.getJarFile(jarUri);
337 if(DEBUG) {
338 System.err.println("TempJarCache: addResources: "+jarUri+": nativeJar "+jarFile.getName());
339 }
340 validateCertificates(certClass, jarFile);
341 JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile,
342 null /* nativeLibraryPath */, false, false, true);
343 resourceFileJars.put(jarUri, LoadState.LOADED);
344 } else if( !testLoadState(resourceFileJarsLS, LoadState.LOADED) ) {
345 throw new IOException("TempJarCache: addResources: "+jarUri+", previous load attempt failed");
346 }
347 }
348
349 /**
350 * Adds all types, native libraries, class files and other files (resources)
351 * if not yet added.
352 *
353 * TODO class access pending
354 * needs Classloader.defineClass(..) access, ie. own derivation - will do when needed ..
355 *
356 * @param certClass if class is certified, the JarFile entries needs to have the same certificate
357 * @param jarUri
358 * @throws IOException if the <code>jarUri</code> could not be loaded or a previous load attempt failed
359 * @throws SecurityException
360 * @throws URISyntaxException
361 * @throws IllegalArgumentException
362 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
363 */
364 public synchronized static final void addAll(final Class<?> certClass, final Uri jarUri) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
365 checkInitialized(false);
366 if(null == jarUri) {
367 throw new IllegalArgumentException("jarUri is null");
368 }
369 final LoadState nativeLibJarsLS = nativeLibJars.get(jarUri);
370 final LoadState classFileJarsLS = classFileJars.get(jarUri);
371 final LoadState resourceFileJarsLS = resourceFileJars.get(jarUri);
372 if( !testLoadState(nativeLibJarsLS, LoadState.LOOKED_UP) ||
373 !testLoadState(classFileJarsLS, LoadState.LOOKED_UP) ||
374 !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP) ) {
375
376 final boolean extractNativeLibraries = staticTempIsExecutable && !testLoadState(nativeLibJarsLS, LoadState.LOADED);
377 final boolean extractClassFiles = !testLoadState(classFileJarsLS, LoadState.LOADED);
378 final boolean extractOtherFiles = !testLoadState(resourceFileJarsLS, LoadState.LOOKED_UP);
379
380 // mark looked-up (those who are not loaded)
381 if(extractNativeLibraries) {
382 nativeLibJars.put(jarUri, LoadState.LOOKED_UP);
383 }
384 if(extractClassFiles) {
385 classFileJars.put(jarUri, LoadState.LOOKED_UP);
386 }
387 if(extractOtherFiles) {
388 resourceFileJars.put(jarUri, LoadState.LOOKED_UP);
389 }
390
391 final JarFile jarFile = JarUtil.getJarFile(jarUri);
392 if(DEBUG) {
393 System.err.println("TempJarCache: addAll: "+jarUri+": nativeJar "+jarFile.getName());
394 }
395 validateCertificates(certClass, jarFile);
396 JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile,
397 null /* nativeLibraryPath */, extractNativeLibraries, extractClassFiles, extractOtherFiles);
398
399 // mark loaded (those were just loaded)
400 if(extractNativeLibraries) {
401 nativeLibJars.put(jarUri, LoadState.LOADED);
402 }
403 if(extractClassFiles) {
404 classFileJars.put(jarUri, LoadState.LOADED);
405 }
406 if(extractOtherFiles) {
407 resourceFileJars.put(jarUri, LoadState.LOADED);
408 }
409 } else if( !testLoadState(nativeLibJarsLS, LoadState.LOADED) ||
410 !testLoadState(classFileJarsLS, LoadState.LOADED) ||
411 !testLoadState(resourceFileJarsLS, LoadState.LOADED) ) {
412 throw new IOException("TempJarCache: addAll: "+jarUri+", previous load attempt failed");
413 }
414 }
415
416 /**
417 * If {@link #isInitialized(boolean) isInitialized(true)} is false due to lack of executable support only,
418 * this method always returns false.
419 * @param libName
420 * @return the found native library path within this cache or null if not found
421 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
422 */
423 public synchronized static final String findLibrary(final String libName) {
424 checkInitialized(false);
425 if( !staticTempIsExecutable ) {
426 return null;
427 }
428 // try with mapped library basename first
429 String path = nativeLibMap.get(libName);
430 if(null == path) {
431 // if valid library name, try absolute path in temp-dir
432 if(null != NativeLibrary.isValidNativeLibraryName(libName, false)) {
433 final File f = new File(tmpFileCache.getTempDir(), libName);
434 if(f.exists()) {
435 path = f.getAbsolutePath();
436 }
437 }
438 }
439 return path;
440 }
441
442 /** TODO class access pending
443 * needs Classloader.defineClass(..) access, ie. own derivation - will do when needed ..
444 public static Class<?> findClass(String name, ClassLoader cl) throws IOException, ClassFormatError {
445 checkInitialized();
446 final File f = new File(nativeTmpFileCache.getTempDir(), IOUtil.getClassFileName(name));
447 if(f.exists()) {
448 Class.forName(fname, initialize, loader)
449 URL url = new URL(f.getAbsolutePath());
450 byte[] b = IOUtil.copyStream2ByteArray(new BufferedInputStream( url.openStream() ));
451 MyClassLoader mcl = new MyClassLoader(cl);
452 return mcl.defineClass(name, b, 0, b.length);
453 }
454 return null;
455 } */
456
457 /**
458 * Similar to {@link ClassLoader#getResource(String)}.
459 * @param name
460 * @return
461 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
462 */
463 public synchronized static final String findResource(final String name) {
464 checkInitialized(false);
465 final File f = new File(tmpFileCache.getTempDir(), name);
466 if(f.exists()) {
467 return f.getAbsolutePath();
468 }
469 return null;
470 }
471
472 /**
473 * Similar to {@link ClassLoader#getResource(String)}.
474 * @param name
475 * @return
476 * @throws URISyntaxException
477 * @throws JogampRuntimeException if not {@link #isInitialized(boolean) isInitialized(false)}
478 */
479 public synchronized static final Uri getResourceUri(final String name) throws URISyntaxException {
480 checkInitialized(false);
481 final File f = new File(tmpFileCache.getTempDir(), name);
482 if(f.exists()) {
483 return Uri.valueOf(f);
484 }
485 return null;
486 }
487
488 private static void validateCertificates(final Class<?> certClass, final JarFile jarFile) throws IOException, SecurityException {
489 if(null == certClass) {
490 throw new IllegalArgumentException("certClass is null");
491 }
492 final Certificate[] rootCerts = SecurityUtil.getCerts(certClass);
493 if( null != rootCerts ) {
494 // Only validate the jarFile's certs with ours, if we have any.
495 // Otherwise we may run uncertified JARs (application).
496 // In case one tries to run uncertified JARs, the wrapping applet/JNLP
497 // SecurityManager will kick in and throw a SecurityException.
498 JarUtil.validateCertificates(rootCerts, jarFile);
499 if(DEBUG) {
500 System.err.println("TempJarCache: validateCertificates: OK - Matching rootCerts in given class "+certClass.getName()+", nativeJar "+jarFile.getName());
501 }
502 } else if(DEBUG) {
503 System.err.println("TempJarCache: validateCertificates: OK - No rootCerts in given class "+certClass.getName()+", nativeJar "+jarFile.getName());
504 }
505 }
506}
A generic unchecked exception for Jogamp errors used throughout the binding as a substitute for Runti...
This class implements an immutable Uri as defined by RFC 2396.
Definition: Uri.java:160
static Uri valueOf(final File file)
Creates a new Uri instance using the given File instance.
Definition: Uri.java:1121
Provides low-level, relatively platform-independent access to shared ("native") libraries.
static final String isValidNativeLibraryName(final String libName, final boolean isLowerCaseAlready)
Comparison of prefix and suffix of the given libName's basename is performed case insensitive
static final int extract(final File dest, final Map< String, String > nativeLibMap, final JarFile jarFile, final String nativeLibraryPath, final boolean extractNativeLibraries, final boolean extractClassFiles, final boolean extractOtherFiles)
Extract the files of the given jar file.
Definition: JarUtil.java:549
static final void validateCertificates(final Certificate[] rootCerts, final JarFile jarFile)
Validate the certificates for each native Lib in the jar file.
Definition: JarUtil.java:696
static JarFile getJarFile(final String clazzBinName, final ClassLoader cl)
Definition: JarUtil.java:378
static final Certificate[] getCerts(final Class<?> clz)
static boolean initSingleton()
Documented way to kick off static initialization.
Static Jar file cache handler using an underlying instance of TempFileCache, see getTempFileCache().
static synchronized final String findResource(final String name)
TODO class access pending needs Classloader.defineClass(..) access, ie.
static synchronized final void addAll(final Class<?> certClass, final Uri jarUri)
Adds all types, native libraries, class files and other files (resources) if not yet added.
static synchronized final void addClasses(final Class<?> certClass, final Uri jarUri)
Adds native classes, if not yet added.
static boolean isInitialized(final boolean forExecutables)
static synchronized boolean checkResources(final Uri jarUri, final LoadState exp)
static synchronized final void addResources(final Class<?> certClass, final Uri jarUri)
Adds native resources, if not yet added.
static synchronized boolean checkClasses(final Uri jarUri, final LoadState exp)
static boolean initSingleton()
Documented way to kick off static initialization.
static synchronized final Uri getResourceUri(final String name)
Similar to ClassLoader#getResource(String).
static synchronized final boolean addNativeLibs(final Class<?> certClass, final Uri jarUri, final String nativeLibraryPath)
Adds native libraries, if not yet added.
static synchronized boolean checkNativeLibs(final Uri jarUri, final LoadState exp)
static synchronized final String findLibrary(final String libName)
If isInitialized(true) is false due to lack of executable support only, this method always returns fa...