GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
IOUtil.java
Go to the documentation of this file.
1/**
2 * Copyright 2010 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 */
28
29package com.jogamp.common.util;
30
31import java.io.BufferedInputStream;
32import java.io.BufferedOutputStream;
33import java.io.Closeable;
34import java.io.File;
35import java.io.FileOutputStream;
36import java.io.FilePermission;
37import java.io.FileWriter;
38import java.io.IOException;
39import java.io.InputStream;
40import java.io.OutputStream;
41import java.io.PrintStream;
42import java.io.Reader;
43import java.io.SyncFailedException;
44import java.lang.ref.WeakReference;
45import java.lang.reflect.Constructor;
46import java.lang.reflect.Method;
47import java.net.URISyntaxException;
48import java.net.URL;
49import java.net.URLConnection;
50import java.nio.ByteBuffer;
51import java.security.PrivilegedAction;
52import java.util.ArrayList;
53import java.util.List;
54import java.util.Locale;
55import java.util.regex.Pattern;
56
57import jogamp.common.Debug;
58import jogamp.common.os.AndroidUtils;
59import jogamp.common.os.PlatformPropsImpl;
60
61import com.jogamp.common.ExceptionUtils;
62import com.jogamp.common.JogampRuntimeException;
63import com.jogamp.common.net.AssetURLContext;
64import com.jogamp.common.net.Uri;
65import com.jogamp.common.nio.Buffers;
66import com.jogamp.common.os.MachineDataInfo;
67import com.jogamp.common.os.Platform;
68
69public class IOUtil {
70 public static final boolean DEBUG;
71 private static final boolean DEBUG_EXE;
72 private static final boolean DEBUG_EXE_NOSTREAM;
73 private static final boolean DEBUG_EXE_EXISTING_FILE;
74 private static final boolean testTempDirExec;
75 private static final Method fileToPathGetter;
76 private static final Method isExecutableQuery;
77 private static final boolean useNativeExeFile;
78
79 static {
80 final boolean _props[] = { false, false, false, false, false, false };
81 final Method[] res = SecurityUtil.doPrivileged(new PrivilegedAction<Method[]>() {
82 @Override
83 public Method[] run() {
84 final Method[] res = new Method[] { null, null };
85 try {
86 int i=0;
87 _props[i++] = Debug.debug("IOUtil");
88 _props[i++] = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe", true);
89 _props[i++] = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.NoStream", true);
90 // For security reasons, we have to hardcode this, i.e. disable this manual debug feature!
91 _props[i++] = false; // PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.ExistingFile", true);
92 _props[i++] = PropertyAccess.getBooleanProperty("jogamp.gluegen.TestTempDirExec", true, true);
93 _props[i++] = PropertyAccess.getBooleanProperty("jogamp.gluegen.UseNativeExeFile", true, false);
94
95 // Java 1.7
96 i=0;
97 res[i] = File.class.getDeclaredMethod("toPath");
98 res[i++].setAccessible(true);
99 final Class<?> nioPathClz = ReflectionUtil.getClass("java.nio.file.Path", false, IOUtil.class.getClassLoader());
100 final Class<?> nioFilesClz = ReflectionUtil.getClass("java.nio.file.Files", false, IOUtil.class.getClassLoader());
101 res[i] = nioFilesClz.getDeclaredMethod("isExecutable", nioPathClz);
102 res[i++].setAccessible(true);
103 } catch (final Throwable t) {
104 if(_props[0]) {
105 ExceptionUtils.dumpThrowable("ioutil-init", t);
106 }
107 }
108 return res;
109 }
110 });
111 {
112 int i=0;
113 DEBUG = _props[i++];
114 DEBUG_EXE = _props[i++];
115 DEBUG_EXE_NOSTREAM = _props[i++];
116 DEBUG_EXE_EXISTING_FILE = _props[i++];
117 testTempDirExec = _props[i++];
118 useNativeExeFile = _props[i++];
119
120 i=0;
121 fileToPathGetter = res[i++];
122 isExecutableQuery = res[i++];
123 }
124 }
125
126 /** Std. temporary directory property key <code>java.io.tmpdir</code>. */
127 private static final String java_io_tmpdir_propkey = "java.io.tmpdir";
128 private static final String user_home_propkey = "user.home";
129 private static final String XDG_CACHE_HOME_envkey = "XDG_CACHE_HOME";
130
131 /** Subdirectory within platform's temporary root directory where all JogAmp related temp files are being stored: {@code jogamp} */
132 public static final String tmpSubDir = "jogamp";
133
134 private IOUtil() {}
135
136 /**
137 * Since usage of {@link java.io.FileOutputStream} is considered security critical,
138 * we need to check it's availability for each use.
139 * <p>
140 * In case a SecurityManager is installed, privileged access is required.
141 * </p>
142 *
143 * @return the constructor of {@link java.io.FileOutputStream} if available and
144 * no SecurityManager is installed or privileged access is granted.
145 * Otherwise null.
146 *
147 */
148 private static final Constructor<?> getFOSCtor() {
149 Constructor<?> _fosCtor;
150 Throwable _t;
151 try {
152 _fosCtor = ReflectionUtil.getConstructor("java.io.FileOutputStream", new Class<?>[] { File.class }, true, IOUtil.class.getClassLoader());
153 _t = null;
154 } catch (final Throwable t) {
155 _fosCtor = null;
156 _t = t;
157 }
158 if(DEBUG) {
159 System.err.println("IOUtil: java.io.FileOutputStream available: "+(null != _fosCtor));
160 if(null!=_t) {
161 _t.printStackTrace();
162 }
163 }
164 return _fosCtor;
165 }
166
167 /***
168 *
169 * STREAM COPY STUFF
170 *
171 */
172
173 /**
174 * Copy the complete specified URL resource to the specified output file. The total
175 * number of bytes written is returned.
176 *
177 * @param conn the open URLConnection
178 * @param outFile the destination
179 * @return
180 * @throws IOException
181 */
182 public static int copyURLConn2File(final URLConnection conn, final File outFile) throws IOException {
183 conn.connect(); // redundant
184
185 int totalNumBytes = 0;
186 final InputStream in = new BufferedInputStream(conn.getInputStream());
187 try {
188 totalNumBytes = copyStream2File(in, outFile);
189 } finally {
190 in.close();
191 }
192 return totalNumBytes;
193 }
194
195 /**
196 * Copy the complete specified input stream to the specified output file. The total
197 * number of bytes written is returned.
198 *
199 * @param in the source
200 * @param outFile the destination
201 * @return
202 * @throws IOException
203 */
204 public static int copyStream2File(final InputStream in, final File outFile) throws IOException {
205 final OutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
206 try {
207 return copyStream2Stream(in, out);
208 } finally {
209 out.close();
210 }
211 }
212
213 /**
214 * Copy the complete specified input stream to the specified output stream. The total
215 * number of bytes written is returned.
216 *
217 * @param in the source
218 * @param out the destination
219 * @return
220 * @throws IOException
221 */
222 public static int copyStream2Stream(final InputStream in, final OutputStream out) throws IOException {
223 return copyStream2Stream(Platform.getMachineDataInfo().pageSizeInBytes(), in, out);
224 }
225
226 /**
227 * Copy the complete specified input stream to the specified output stream. The total
228 * number of bytes written is returned.
229 *
230 * @param bufferSize the intermediate buffer size, should be {@link MachineDataInfo#pageSizeInBytes()} for best performance.
231 * @param in the source
232 * @param out the destination
233 * @return
234 * @throws IOException
235 */
236 public static int copyStream2Stream(final int bufferSize, final InputStream in, final OutputStream out) throws IOException {
237 final byte[] buf = new byte[bufferSize];
238 int numBytes = 0;
239 while (true) {
240 int count;
241 if ((count = in.read(buf)) == -1) {
242 break;
243 }
244 out.write(buf, 0, count);
245 numBytes += count;
246 }
247 return numBytes;
248 }
249
250 public static StringBuilder appendCharStream(final StringBuilder sb, final Reader r) throws IOException {
251 final char[] cbuf = new char[1024];
252 int count;
253 while( 0 < ( count = r.read(cbuf) ) ) {
254 sb.append(cbuf, 0, count);
255 }
256 return sb;
257 }
258
259 /**
260 * Copy the complete specified input stream to a byte array, which is being returned.
261 */
262 public static byte[] copyStream2ByteArray(InputStream stream) throws IOException {
263 if( !(stream instanceof BufferedInputStream) ) {
264 stream = new BufferedInputStream(stream);
265 }
266 int totalRead = 0;
267 int avail = stream.available();
268 byte[] data = new byte[avail];
269 int numRead = 0;
270 do {
271 if (totalRead + avail > data.length) {
272 final byte[] newData = new byte[totalRead + avail];
273 System.arraycopy(data, 0, newData, 0, totalRead);
274 data = newData;
275 }
276 numRead = stream.read(data, totalRead, avail);
277 if (numRead >= 0) {
278 totalRead += numRead;
279 }
280 avail = stream.available();
281 } while (avail > 0 && numRead >= 0);
282
283 // just in case the announced avail > totalRead
284 if (totalRead != data.length) {
285 final byte[] newData = new byte[totalRead];
286 System.arraycopy(data, 0, newData, 0, totalRead);
287 data = newData;
288 }
289 return data;
290 }
291
292 /**
293 * Copy the complete specified input stream to a NIO ByteBuffer w/ native byte order, which is being returned.
294 *
295 * @param stream input stream, which will be wrapped into a BufferedInputStream, if not already done.
296 */
297 public static ByteBuffer copyStream2ByteBuffer(final InputStream stream) throws IOException {
298 return copyStream2ByteBuffer(stream, -1);
299 }
300
301 /**
302 * Copy the complete specified input stream to a NIO ByteBuffer w/ native byte order, which is being returned.
303 *
304 * @param stream input stream, which will be wrapped into a BufferedInputStream, if not already done.
305 * @param initialCapacity initial buffer capacity in bytes, if &lt; currently available bytes, initial buffer capacity is set to currently available bytes.
306 */
307 public static ByteBuffer copyStream2ByteBuffer(InputStream stream, int initialCapacity) throws IOException {
308 if( !(stream instanceof BufferedInputStream) ) {
309 stream = new BufferedInputStream(stream);
310 }
311 int avail = stream.available();
312 if( initialCapacity < avail ) {
313 initialCapacity = avail;
314 }
316 ByteBuffer data = Buffers.newDirectByteBuffer( machine.pageAlignedSize( initialCapacity ) );
317 final byte[] chunk = new byte[machine.pageSizeInBytes()];
318 int chunk2Read = Math.min(machine.pageSizeInBytes(), avail);
319 int numRead = 0;
320 do {
321 if (avail > data.remaining()) {
322 final ByteBuffer newData = Buffers.newDirectByteBuffer(
323 machine.pageAlignedSize(data.position() + avail) );
324 newData.put(data);
325 data = newData;
326 }
327
328 numRead = stream.read(chunk, 0, chunk2Read);
329 if (numRead > 0) {
330 data.put(chunk, 0, numRead);
331 }
332 avail = stream.available();
333 chunk2Read = Math.min(machine.pageSizeInBytes(), avail);
334 } while ( numRead > 0 ); // EOS: -1 == numRead, EOF maybe reached earlier w/ 0 == numRead
335
336 data.flip();
337 return data;
338 }
339
340 /**
341 * Copy the specified input stream chunk to a NIO ByteBuffer w/ native byte order, which is being returned.
342 *
343 * @param stream input stream, which will be wrapped into a BufferedInputStream, if not already done.
344 * @param skipBytes initial bytes to skip from input stream.
345 * @param byteCount bytes to copy starting after skipBytes.
346 */
347 public static ByteBuffer copyStreamChunk2ByteBuffer(InputStream stream, int skipBytes, int byteCount) throws IOException {
348 if( !(stream instanceof BufferedInputStream) ) {
349 stream = new BufferedInputStream(stream);
350 }
352 final ByteBuffer data = Buffers.newDirectByteBuffer( machine.pageAlignedSize( byteCount ) );
353 final byte[] chunk = new byte[machine.pageSizeInBytes()];
354 int numRead = 1; // EOS: -1 == numRead, EOF maybe reached earlier w/ 0 == numRead
355 while ( numRead > 0 && skipBytes > 0 ) {
356 final int chunk2Read = Math.min(machine.pageSizeInBytes(), skipBytes);
357 numRead = stream.read(chunk, 0, chunk2Read);
358 skipBytes -= numRead;
359 }
360 while ( numRead > 0 && byteCount > 0 ) {
361 final int chunk2Read = Math.min(machine.pageSizeInBytes(), byteCount);
362 numRead = stream.read(chunk, 0, chunk2Read);
363 if (numRead > 0) {
364 data.put(chunk, 0, numRead);
365 }
366 byteCount -= numRead;
367 }
368
369 data.flip();
370 return data;
371 }
372
373 /***
374 *
375 * RESOURCE / FILE NAME STUFF
376 *
377 */
378
379 private static final Pattern patternSingleBS = Pattern.compile("\\\\{1}");
380
381 /**
382 *
383 * @param path
384 * @param startWithSlash
385 * @param endWithSlash
386 * @return
387 * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
388 */
389 public static String slashify(final String path, final boolean startWithSlash, final boolean endWithSlash) throws URISyntaxException {
390 String p = patternSingleBS.matcher(path).replaceAll("/");
391 if (startWithSlash && !p.startsWith("/")) {
392 p = "/" + p;
393 }
394 if (endWithSlash && !p.endsWith("/")) {
395 p = p + "/";
396 }
397 return cleanPathString(p);
398 }
399
400 /**
401 * Returns the lowercase suffix of the given file name (the text
402 * after the last '.' in the file name). Returns null if the file
403 * name has no suffix. Only operates on the given file name;
404 * performs no I/O operations.
405 *
406 * @param file name of the file
407 * @return lowercase suffix of the file name
408 * @throws NullPointerException if file is null
409 */
410
411 public static String getFileSuffix(final File file) {
412 return getFileSuffix(file.getName());
413 }
414
415 /**
416 * Returns the lowercase suffix of the given file name (the text
417 * after the last '.' in the file name). Returns null if the file
418 * name has no suffix. Only operates on the given file name;
419 * performs no I/O operations.
420 *
421 * @param filename name of the file
422 * @return lowercase suffix of the file name
423 * @throws NullPointerException if filename is null
424 */
425 public static String getFileSuffix(final String filename) {
426 final int lastDot = filename.lastIndexOf('.');
427 if (lastDot < 0) {
428 return null;
429 }
430 return toLowerCase(filename.substring(lastDot + 1));
431 }
432 private static String toLowerCase(final String arg) {
433 if (arg == null) {
434 return null;
435 }
436
437 return arg.toLowerCase();
438 }
439
440 /***
441 * @param file
442 * @param allowOverwrite
443 * @return outputStream The resulting output stream
444 * @throws IOException if the file already exists and <code>allowOverwrite</code> is false,
445 * the class {@link java.io.FileOutputStream} is not accessible or
446 * the user does not have sufficient rights to access the local filesystem.
447 */
448 public static FileOutputStream getFileOutputStream(final File file, final boolean allowOverwrite) throws IOException {
449 final Constructor<?> fosCtor = getFOSCtor();
450 if(null == fosCtor) {
451 throw new IOException("Cannot open file (" + file + ") for writing, FileOutputStream feature not available.");
452 }
453 if (file.exists() && !allowOverwrite) {
454 throw new IOException("File already exists (" + file + ") and overwrite=false");
455 }
456 try {
457 return (FileOutputStream) fosCtor.newInstance(new Object[] { file });
458 } catch (final Exception e) {
459 throw new IOException("error opening " + file + " for write. ", e);
460 }
461 }
462
463 public static String getClassFileName(final String clazzBinName) {
464 // or return clazzBinName.replace('.', File.separatorChar) + ".class"; ?
465 return clazzBinName.replace('.', '/') + ".class";
466 }
467
468 /**
469 * @param clazzBinName com.jogamp.common.util.cache.TempJarCache
470 * @param cl ClassLoader to locate the JarFile
471 * @return jar:file:/usr/local/projects/JOGL/gluegen/build-x86_64/gluegen-rt.jar!/com/jogamp/common/util/cache/TempJarCache.class
472 * @throws IOException if the jar file could not been found by the ClassLoader
473 */
474 public static URL getClassURL(final String clazzBinName, final ClassLoader cl) throws IOException {
475 final URL url = cl.getResource(getClassFileName(clazzBinName));
476 if(null == url) {
477 throw new IOException("Cannot not find: "+clazzBinName);
478 }
479 return url;
480 }
481
482 /**
483 * Returns the basename of the given fname w/o directory part
484 * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
485 */
486 public static String getBasename(String fname) throws URISyntaxException {
487 fname = slashify(fname, false /* startWithSlash */, false /* endWithSlash */);
488 final int lios = fname.lastIndexOf('/'); // strip off dirname
489 if(lios>=0) {
490 fname = fname.substring(lios+1);
491 }
492 return fname;
493 }
494
495 /**
496 * Returns unified '/' dirname including the last '/'
497 * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
498 */
499 public static String getDirname(String fname) throws URISyntaxException {
500 fname = slashify(fname, false /* startWithSlash */, false /* endWithSlash */);
501 final int lios = fname.lastIndexOf('/'); // strip off dirname
502 if(lios>=0) {
503 fname = fname.substring(0, lios+1);
504 }
505 return fname;
506 }
507
508 /***
509 *
510 * RESOURCE LOCATION HELPER
511 *
512 */
513
514 /**
515 * Helper compound associating a class instance and resource paths
516 * to be {@link #resolve(int) resolved} at a later time.
517 */
518 public static class ClassResources {
519 /** Optional {@link ClassLoader} used to {@link #resolve(int)} {@link #resourcePaths}. */
520 public final ClassLoader classLoader;
521
522 /** Optional class instance used to {@link #resolve(int)} relative {@link #resourcePaths}. */
523 public final Class<?> contextCL;
524
525 /** Resource paths, see {@link #resolve(int)}. */
526 public final String[] resourcePaths;
527
528 /** Returns the number of resources, i.e. <code>resourcePaths.length</code>. */
529 public final int resourceCount() { return resourcePaths.length; }
530
531 /**
532 * @param resourcePaths multiple relative or absolute resource locations
533 * @param classLoader optional {@link ClassLoader}, see {@link IOUtil#getResource(String, ClassLoader, Class)}
534 * @param relContext optional relative context, see {@link IOUtil#getResource(String, ClassLoader, Class)}
535 */
536 public ClassResources(final String[] resourcePaths, final ClassLoader classLoader, final Class<?> relContext) {
537 for(int i=resourcePaths.length-1; i>=0; i--) {
538 if( null == resourcePaths[i] ) {
539 throw new IllegalArgumentException("resourcePath["+i+"] is null");
540 }
541 }
542 this.classLoader = classLoader;
543 this.contextCL = relContext;
544 this.resourcePaths = resourcePaths;
545 }
546
547 /**
548 * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using
549 * {@link #classLoader}, {@link #contextCL} through {@link IOUtil#getResource(String, ClassLoader, Class)}.
550 * @throws ArrayIndexOutOfBoundsException if <code>uriIndex</code> is < 0 or >= {@link #resourceCount()}.
551 */
552 public URLConnection resolve(final int uriIndex) throws ArrayIndexOutOfBoundsException {
554 }
555 }
556
557 /**
558 * Locating a resource using {@link #getResource(String, ClassLoader)}:
559 * <ul>
560 * <li><i>relative</i>: <code>relContext</code>'s package name-path plus <code>resourcePath</code> via <code>classLoader</code>.
561 * This allows locations relative to JAR- and other URLs.
562 * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder.
563 * This attempt is skipped if {@code relContext} is {@code null}.</li>
564 * <li><i>absolute</i>: <code>resourcePath</code> as is via <code>classLoader</code>.
565 * </ul>
566 * <p>
567 * Returns the resolved and open URLConnection or null if not found.
568 * </p>
569 *
570 * @param resourcePath the resource path to locate relative or absolute
571 * @param classLoader the optional {@link ClassLoader}, recommended
572 * @param relContext relative context, i.e. position, of the {@code resourcePath},
573 * to perform the relative lookup, if not {@code null}.
574 * @see #getResource(String, ClassLoader)
575 * @see ClassLoader#getResource(String)
576 * @see ClassLoader#getSystemResource(String)
577 */
578 public static URLConnection getResource(final String resourcePath, final ClassLoader classLoader, final Class<?> relContext) {
579 if(null == resourcePath) {
580 return null;
581 }
582 URLConnection conn = null;
583 if(null != relContext) {
584 // scoping the path within the class's package
585 final String className = relContext.getName().replace('.', '/');
586 final int lastSlash = className.lastIndexOf('/');
587 if (lastSlash >= 0) {
588 final String pkgName = className.substring(0, lastSlash + 1);
589 conn = getResource(pkgName + resourcePath, classLoader);
590 if(DEBUG) {
591 System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+relContext.getName()+">: "+(null!=conn));
592 }
593 }
594 } else if(DEBUG) {
595 System.err.println("IOUtil: null context, skip rel. lookup");
596 }
597 if(null == conn) {
598 conn = getResource(resourcePath, classLoader);
599 if(DEBUG) {
600 System.err.println("IOUtil: found <"+resourcePath+"> by classloader: "+(null!=conn));
601 }
602 }
603 return conn;
604 }
605
606 /**
607 * Locating a resource using the ClassLoader's facilities.
608 * <p>
609 * Returns the resolved and connected URLConnection or null if not found.
610 * </p>
611 *
612 * @see ClassLoader#getResource(String)
613 * @see ClassLoader#getSystemResource(String)
614 * @see URL#URL(String)
615 * @see File#File(String)
616 */
617 public static URLConnection getResource(final String resourcePath, final ClassLoader cl) {
618 if(null == resourcePath) {
619 return null;
620 }
621 if(DEBUG) {
622 System.err.println("IOUtil: locating <"+resourcePath+">, has cl: "+(null!=cl));
623 }
624 if(resourcePath.startsWith(AssetURLContext.asset_protocol_prefix)) {
625 try {
626 return AssetURLContext.createURL(resourcePath, cl).openConnection();
627 } catch (final IOException ioe) {
628 if(DEBUG) {
629 ExceptionUtils.dumpThrowable("IOUtil", ioe);
630 }
631 return null;
632 }
633 } else {
634 try {
635 return AssetURLContext.resolve(resourcePath, cl);
636 } catch (final IOException ioe) {
637 if(DEBUG) {
638 ExceptionUtils.dumpThrowable("IOUtil", ioe);
639 }
640 }
641 }
642 return null;
643 }
644
645 /**
646 * Generates a path for the 'relativeFile' relative to the 'baseLocation'.
647 *
648 * @param baseLocation denotes a directory
649 * @param relativeFile denotes a relative file to the baseLocation
650 * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
651 */
652 public static String getRelativeOf(final File baseLocation, final String relativeFile) throws URISyntaxException {
653 if(null == relativeFile) {
654 return null;
655 }
656
657 if (baseLocation != null) {
658 final File file = new File(baseLocation, relativeFile);
659 // Handle things on Windows
660 return slashify(file.getPath(), false /* startWithSlash */, false /* endWithSlash */);
661 }
662 return null;
663 }
664
665 /**
666 * @param path assuming a slashified path, either denotes a file or directory, either relative or absolute.
667 * @return parent of path
668 * @throws URISyntaxException if path is empty or has no parent directory available
669 */
670 public static String getParentOf(final String path) throws URISyntaxException {
671 final int pl = null!=path ? path.length() : 0;
672 if(pl == 0) {
673 throw new IllegalArgumentException("path is empty <"+path+">");
674 }
675
676 final int e = path.lastIndexOf("/");
677 if( e < 0 ) {
678 throw new URISyntaxException(path, "path contains no '/': <"+path+">");
679 }
680 if( e == 0 ) {
681 // path is root directory
682 throw new URISyntaxException(path, "path has no parents: <"+path+">");
683 }
684 if( e < pl - 1 ) {
685 // path is file, return it's parent directory
686 return path.substring(0, e+1);
687 }
688 final int j = path.lastIndexOf("!") + 1; // '!' Separates JARFile entry -> local start of path
689 // path is a directory ..
690 final int p = path.lastIndexOf("/", e-1);
691 if( p >= j) {
692 // parent itself has '/' - post '!' or no '!' at all
693 return path.substring(0, p+1);
694 } else {
695 // parent itself has no '/'
696 final String parent = path.substring(j, e);
697 if( parent.equals("..") ) {
698 throw new URISyntaxException(path, "parent is unresolved: <"+path+">");
699 } else {
700 // parent is '!' or empty (relative path)
701 return path.substring(0, j);
702 }
703 }
704 }
705
706 /**
707 * @param path assuming a slashified path, either denoting a file or directory, either relative or absolute.
708 * @return clean path string where {@code ./} and {@code ../} is resolved,
709 * while keeping a starting {@code ../} at the beginning of a relative path.
710 * @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
711 */
712 public static String cleanPathString(String path) throws URISyntaxException {
713 // Resolve './' before '../' to handle case 'parent/./../a.txt' properly.
714 int idx = path.length() - 1;
715 while ( idx >= 1 && ( idx = path.lastIndexOf("./", idx) ) >= 0 ) {
716 if( 0 < idx && path.charAt(idx-1) == '.' ) {
717 idx-=2; // skip '../' -> idx upfront
718 } else {
719 path = path.substring(0, idx) + path.substring(idx+2);
720 idx--; // idx upfront
721 }
722 }
723 idx = 0;
724 while ( ( idx = path.indexOf("../", idx) ) >= 0 ) {
725 if( 0 == idx ) {
726 idx += 3; // skip starting '../'
727 } else {
728 path = getParentOf(path.substring(0, idx)) + path.substring(idx+3);
729 idx = 0;
730 }
731 }
732 return path;
733 }
734
735 public static final Pattern patternSpaceEnc = Pattern.compile("%20");
736
737 /**
738 * If <code>uri</code> is a <i>file scheme</i>
739 * implementation returns {@link Uri#toFile()}.{@link File#getPath()}.
740 * <p>
741 * Otherwise it returns the {@link URI#toASCIIString()} encoded URI.
742 * </p>
743 */
744 public static String getUriFilePathOrASCII(final Uri uri) {
745 if( uri.isFileScheme() ) {
746 return uri.toFile().getPath();
747 } else {
748 return uri.toASCIIString().get();
749 }
750 }
751
752 /**
753 * Returns the connected URLConnection, or null if not url is not available
754 */
755 public static URLConnection openURL(final URL url) {
756 return openURL(url, ".");
757 }
758
759 /**
760 * Returns the connected URLConnection, or null if not url is not available
761 */
762 public static URLConnection openURL(final URL url, final String dbgmsg) {
763 if(null!=url) {
764 try {
765 final URLConnection c = url.openConnection();
766 c.connect(); // redundant
767 if(DEBUG) {
768 System.err.println("IOUtil: urlExists("+url+") ["+dbgmsg+"] - true");
769 }
770 return c;
771 } catch (final IOException ioe) {
772 if(DEBUG) {
773 ExceptionUtils.dumpThrowable("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false -", ioe);
774 }
775 }
776 } else if(DEBUG) {
777 System.err.println("IOUtil: no url - urlExists(null) ["+dbgmsg+"]");
778 }
779
780 return null;
781 }
782
783 private static String getExeTestFileSuffix() {
784 switch(PlatformPropsImpl.OS_TYPE) {
785 case WINDOWS:
786 if( useNativeExeFile && Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) {
787 return ".exe";
788 } else {
789 return ".bat";
790 }
791 default:
792 return ".sh";
793 }
794 }
795 private static String getExeTestShellCode() {
796 switch(PlatformPropsImpl.OS_TYPE) {
797 case WINDOWS:
798 return "echo off"+PlatformPropsImpl.NEWLINE;
799 default:
800 return "#!/bin/true"+PlatformPropsImpl.NEWLINE;
801 }
802 }
803 private static String getExeNativePath(final String canonicalPath) {
804 switch(PlatformPropsImpl.OS_TYPE) {
805 case WINDOWS:
806 return "\""+canonicalPath+"\"";
807 default:
808 return canonicalPath;
809 }
810 }
811 private static String[] getExeTestCommandArgs(final String scriptFile) {
812 switch(PlatformPropsImpl.OS_TYPE) {
813 case WINDOWS:
814 // return new String[] { "cmd", "/c", scriptFile };
815 default:
816 return new String[] { scriptFile };
817 }
818 }
819
820 private static final byte[] readCode(final String fname) throws IOException {
821 final URLConnection con = IOUtil.getResource(fname, IOUtil.class.getClassLoader(), IOUtil.class);
822 final InputStream in = con.getInputStream();
823 byte[] output = null;
824 try {
825 output = CustomCompress.inflateFromStream(in);
826 } finally {
827 in.close();
828 }
829 return output;
830 }
831 private static final Object exeTestLock = new Object();
832 private static WeakReference<byte[]> exeTestCodeRef = null;
833
834 private static void fillExeTestFile(final File exefile) throws IOException {
835 if( useNativeExeFile &&
836 Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE &&
837 Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family
838 ) {
839 final byte[] exeTestCode;
840 synchronized ( exeTestLock ) {
841 byte[] _exeTestCode = null;
842 if( null == exeTestCodeRef || null == ( _exeTestCode = exeTestCodeRef.get() ) ) {
843 final String fname;
844 if( Platform.CPUType.X86_64 == PlatformPropsImpl.CPU_ARCH ) {
845 fname = "bin/exe-windows-x86_64.defl";
846 } else {
847 fname = "bin/exe-windows-i386.defl";
848 }
849 exeTestCode = readCode(fname);
850 exeTestCodeRef = new WeakReference<byte[]>(exeTestCode);
851 } else {
852 exeTestCode = _exeTestCode;
853 }
854 }
855 final FileOutputStream out = new FileOutputStream(exefile);
856 try {
857 out.write(exeTestCode, 0, exeTestCode.length);
858 try {
859 out.getFD().sync();
860 } catch (final SyncFailedException sfe) {
861 ExceptionUtils.dumpThrowable("", sfe);
862 }
863 } finally {
864 out.close();
865 }
866 } else {
867 final String shellCode = getExeTestShellCode();
868 if( isStringSet(shellCode) ) {
869 final FileWriter fout = new FileWriter(exefile);
870 try {
871 fout.write(shellCode);
872 try {
873 fout.flush();
874 } catch (final IOException sfe) {
875 ExceptionUtils.dumpThrowable("", sfe);
876 }
877 } finally {
878 fout.close();
879 }
880 }
881 }
882 }
883 private static boolean getOSHasNoexecFS() {
884 switch(PlatformPropsImpl.OS_TYPE) {
885 case OPENKODE:
886 return false;
887
888 default:
889 return true;
890 }
891 }
892
893 /**
894 * @see <a href="http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html">Free-Desktop - XDG Base Directory Specification</a>
895 */
896 private static boolean getOSHasFreeDesktopXDG() {
897 switch(PlatformPropsImpl.OS_TYPE) {
898 case ANDROID:
899 case MACOS:
900 case IOS:
901 case WINDOWS:
902 case OPENKODE:
903 return false;
904
905 default:
906 return true;
907 }
908 }
909
910 /**
911 * Test whether {@code file} exists and matches the given requirements
912 *
913 * @param file
914 * @param shallBeDir
915 * @param shallBeWritable
916 * @return
917 */
918 public static boolean testFile(final File file, final boolean shallBeDir, final boolean shallBeWritable) {
919 if (!file.exists()) {
920 if(DEBUG) {
921 System.err.println("IOUtil.testFile: <"+file.getAbsolutePath()+">: does not exist");
922 }
923 return false;
924 }
925 if (shallBeDir && !file.isDirectory()) {
926 if(DEBUG) {
927 System.err.println("IOUtil.testFile: <"+file.getAbsolutePath()+">: is not a directory");
928 }
929 return false;
930 }
931 if (shallBeWritable && !file.canWrite()) {
932 if(DEBUG) {
933 System.err.println("IOUtil.testFile: <"+file.getAbsolutePath()+">: is not writable");
934 }
935 return false;
936 }
937 return true;
938 }
939
940 public static class StreamMonitor implements Runnable {
941 private final InputStream[] istreams;
942 private final boolean[] eos;
943 private final PrintStream ostream;
944 private final String prefix;
945 public StreamMonitor(final InputStream[] streams, final PrintStream ostream, final String prefix) {
946 this.istreams = streams;
947 this.eos = new boolean[streams.length];
948 this.ostream = ostream;
949 this.prefix = prefix;
950 final InterruptSource.Thread t = new InterruptSource.Thread(null, this, "StreamMonitor-"+Thread.currentThread().getName());
951 t.setDaemon(true);
952 t.start();
953 }
954
955 @Override
956 public void run()
957 {
958 final byte[] buffer = new byte[4096];
959 try {
960 final int streamCount = istreams.length;
961 int eosCount = 0;
962 do {
963 for(int i=0; i<istreams.length; i++) {
964 if( !eos[i] ) {
965 final int numReadI = istreams[i].read(buffer);
966 if (numReadI > 0) {
967 if( null != ostream ) {
968 if( null != prefix ) {
969 ostream.write(prefix.getBytes());
970 }
971 ostream.write(buffer, 0, numReadI);
972 }
973 } else {
974 // numReadI == -1
975 eosCount++;
976 eos[i] = true;
977 }
978 }
979 }
980 if( null != ostream ) {
981 ostream.flush();
982 }
983 } while ( eosCount < streamCount );
984 } catch (final IOException e) {
985 } finally {
986 if( null != ostream ) {
987 ostream.flush();
988 }
989 // Should allow clean exit when process shuts down
990 }
991 }
992 }
993
994 private static final Boolean isNioExecutableFile(final File file) {
995 if( null != fileToPathGetter && null != isExecutableQuery ) {
996 try {
997 return (Boolean) isExecutableQuery.invoke(null, fileToPathGetter.invoke(file));
998 } catch (final Throwable t) {
999 throw new JogampRuntimeException("error invoking Files.isExecutable(file.toPath())", t);
1000 }
1001 } else {
1002 return null;
1003 }
1004 }
1005
1006 /**
1007 * Returns true if the given {@code dir}
1008 * <ol>
1009 * <li>exists, and</li>
1010 * <li>is a directory, and</li>
1011 * <li>is writeable, and</li>
1012 * <li>files can be executed from the directory</li>
1013 * </ol>
1014 *
1015 * @throws SecurityException if file creation and process execution is not allowed within the current security context
1016 * @param dir
1017 */
1018 public static boolean testDirExec(final File dir)
1019 throws SecurityException
1020 {
1021 final boolean debug = DEBUG_EXE || DEBUG;
1022
1023 if( !testTempDirExec ) {
1024 if(DEBUG) {
1025 System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Disabled TestTempDirExec");
1026 }
1027 return false;
1028 }
1029 if (!testFile(dir, true, true)) {
1030 if( debug ) {
1031 System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Not writeable dir");
1032 }
1033 return false;
1034 }
1035 if(!getOSHasNoexecFS()) {
1036 if( debug ) {
1037 System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Always executable");
1038 }
1039 return true;
1040 }
1041
1042 final long t0 = debug ? System.currentTimeMillis() : 0;
1043 final File exeTestFile;
1044 String exeNativePath;
1045 final boolean existingExe;
1046 try {
1047 final File permExeTestFile = DEBUG_EXE_EXISTING_FILE ? new File(dir, "jogamp_exe_tst"+getExeTestFileSuffix()) : null;
1048 if( null != permExeTestFile && permExeTestFile.exists() ) {
1049 exeTestFile = permExeTestFile;
1050 existingExe = true;
1051 } else {
1052 exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir);
1053 existingExe = false;
1054 fillExeTestFile(exeTestFile);
1055 }
1056 exeNativePath = getExeNativePath( exeTestFile.getCanonicalPath() );
1057 } catch (final SecurityException se) {
1058 throw se; // fwd Security exception
1059 } catch (final IOException e) {
1060 if( debug ) {
1061 e.printStackTrace();
1062 }
1063 return false;
1064 }
1065 final long t1 = debug ? System.currentTimeMillis() : 0;
1066 long t2;
1067 int res = -1;
1068 int exitValue = -1;
1069 Boolean isNioExec = null;
1070 if( existingExe || exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */) ) {
1071 t2 = debug ? System.currentTimeMillis() : 0;
1072 // First soft exec test via NIO's ACL check, if available
1073 isNioExec = isNioExecutableFile(exeTestFile);
1074 if( null != isNioExec ) {
1075 res = isNioExec.booleanValue() ? 0 : -1;
1076 }
1077 if( null == isNioExec || 0 <= res ) {
1078 // Hard exec test via actual execution, if NIO's ACL check succeeded or not available.
1079 // Required, since Windows 'Software Restriction Policies (SRP)' won't be triggered merely by NIO's ACL check.
1080 Process pr = null;
1081 try {
1082 // Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)'
1083 // and hence splitting up command by spaces!
1084 // Note: All no-exec cases throw an IOExceptions at ProcessBuilder.start(), i.e. below exec() call!
1085 pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeNativePath ), null, null );
1086 if( DEBUG_EXE && !DEBUG_EXE_NOSTREAM ) {
1087 new StreamMonitor(new InputStream[] { pr.getInputStream(), pr.getErrorStream() }, System.err, "Exe-Tst: ");
1088 }
1089 pr.waitFor();
1090 exitValue = pr.exitValue(); // Note: Bug 1219 Comment 50: On reporter's machine exit value 1 is being returned
1091 if( 0 == exitValue ) {
1092 res++; // file has been executed and exited normally
1093 } else {
1094 res = -2; // abnormal termination
1095 }
1096 } catch (final SecurityException se) {
1097 throw se; // fwd Security exception
1098 } catch (final Throwable t) {
1099 t2 = debug ? System.currentTimeMillis() : 0;
1100 res = -3;
1101 if( debug ) {
1102 System.err.println("IOUtil.testDirExec: <"+exeNativePath+">: Caught "+t.getClass().getSimpleName()+": "+t.getMessage());
1103 t.printStackTrace();
1104 }
1105 } finally {
1106 if( null != pr ) {
1107 // Bug 1219 Comment 58: Ensure that the launched process gets terminated!
1108 // This is Process implementation specific and varies on different platforms,
1109 // hence it may be required.
1110 try {
1111 pr.destroy();
1112 } catch (final Throwable t) {
1114 }
1115 }
1116 }
1117 }
1118 } else {
1119 t2 = debug ? System.currentTimeMillis() : 0;
1120 }
1121
1122 final boolean ok = 0 <= res;
1123 if( !DEBUG_EXE && !existingExe ) {
1124 exeTestFile.delete();
1125 }
1126 if( debug ) {
1127 final long t3 = System.currentTimeMillis();
1128 System.err.println("IOUtil.testDirExec(): test-exe <"+exeNativePath+">, existingFile "+existingExe+", isNioExec "+isNioExec+", returned "+exitValue);
1129 System.err.println("IOUtil.testDirExec(): abs-path <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok);
1130 System.err.println("IOUtil.testDirExec(): total "+(t3-t0)+"ms, create "+(t1-t0)+"ms, fill "+(t2-t1)+"ms, execute "+(t3-t2)+"ms");
1131 }
1132 return ok;
1133 }
1134
1135 private static File testDirImpl(final File dir, final boolean create, final boolean executable, final String dbgMsg)
1136 throws SecurityException
1137 {
1138 final File res;
1139 if (create && !dir.exists()) {
1140 dir.mkdirs();
1141 }
1142 if( executable ) {
1143 res = testDirExec(dir) ? dir : null;
1144 } else {
1145 res = testFile(dir, true, true) ? dir : null;
1146 }
1147 if(DEBUG) {
1148 System.err.println("IOUtil.testDirImpl("+dbgMsg+"): <"+dir.getAbsolutePath()+">, create "+create+", exec "+executable+": "+(null != res));
1149 }
1150 return res;
1151 }
1152
1153 /**
1154 * Returns the directory {@code dir}, which is processed and tested as described below.
1155 * <ol>
1156 * <li>If {@code create} is {@code true} and the directory does not exist yet, it is created incl. all sub-directories.</li>
1157 * <li>If {@code dirName} exists, but is not a directory, {@code null} is being returned.</li>
1158 * <li>If the directory does not exist or is not writeable, {@code null} is being returned.</li>
1159 * <li>If {@code executable} is {@code true} and files cannot be executed from the directory, {@code null} is being returned.</li>
1160 * </ol>
1161 *
1162 * @param dir the directory to process
1163 * @param create true if the directory shall be created if not existing
1164 * @param executable true if the user intents to launch executables from the temporary directory, otherwise false.
1165 * @throws SecurityException if file creation and process execution is not allowed within the current security context
1166 */
1167 public static File testDir(final File dir, final boolean create, final boolean executable)
1168 throws SecurityException
1169 {
1170 return testDirImpl(dir, create, executable, "testDir");
1171 }
1172
1173 private static boolean isStringSet(final String s) { return null != s && 0 < s.length(); }
1174
1175 /**
1176 * This methods finds [and creates] an available temporary sub-directory:
1177 * <pre>
1178 File tmpBaseDir = null;
1179 if(null != testDir(tmpRoot, true, executable)) { // check tmpRoot first
1180 for(int i = 0; null == tmpBaseDir && i<=9999; i++) {
1181 final String tmpDirSuffix = String.format("_%04d", i); // 4 digits for iteration
1182 tmpBaseDir = testDir(new File(tmpRoot, tmpSubDirPrefix+tmpDirSuffix), true, executable);
1183 }
1184 } else {
1185 tmpBaseDir = null;
1186 }
1187 return tmpBaseDir;
1188 * </pre>
1189 * <p>
1190 * The iteration through [0000-9999] ensures that the code is multi-user save.
1191 * </p>
1192 * @param tmpRoot
1193 * @param executable
1194 * @param dbgMsg
1195 * @param tmpDirPrefix
1196 * @return a temporary directory, writable by this user
1197 * @throws SecurityException
1198 */
1199 private static File getSubTempDir(final File tmpRoot, final String tmpSubDirPrefix, final boolean executable, final String dbgMsg)
1200 throws SecurityException
1201 {
1202 File tmpBaseDir = null;
1203 if(null != testDirImpl(tmpRoot, true /* create */, executable, dbgMsg)) { // check tmpRoot first
1204 for(int i = 0; null == tmpBaseDir && i<=9999; i++) {
1205 final String tmpDirSuffix = String.format((Locale)null, "_%04d", i); // 4 digits for iteration
1206 tmpBaseDir = testDirImpl(new File(tmpRoot, tmpSubDirPrefix+tmpDirSuffix), true /* create */, executable, dbgMsg);
1207 }
1208 }
1209 return tmpBaseDir;
1210 }
1211
1212 private static File getFile(final String fname) {
1213 if( isStringSet(fname) ) {
1214 return new File(fname);
1215 } else {
1216 return null;
1217 }
1218
1219 }
1220 /**
1221 * Returns a platform independent writable directory for temporary files
1222 * consisting of the platform's {@code temp-root} + {@link #tmpSubDir},
1223 * e.g. {@code /tmp/jogamp_0000/}.
1224 * <p>
1225 * On standard Java the {@code temp-root} folder is specified by <code>java.io.tempdir</code>.
1226 * </p>
1227 * <p>
1228 * On Android the {@code temp-root} folder is relative to the applications local folder
1229 * (see {@link Context#getDir(String, int)}) is returned, if
1230 * the Android application/activity has registered it's Application Context
1231 * via {@link jogamp.common.os.android.StaticContext.StaticContext#init(Context, ClassLoader) StaticContext.init(..)}.
1232 * This allows using the temp folder w/o the need for <code>sdcard</code>
1233 * access, which would be the <code>java.io.tempdir</code> location on Android!
1234 * </p>
1235 * <p>
1236 * In case {@code temp-root} is the users home folder,
1237 * a dot is being prepended to {@link #tmpSubDir}, i.e.: {@code /home/user/.jogamp_0000/}.
1238 * </p>
1239 * @param executable true if the user intents to launch executables from the temporary directory, otherwise false.
1240 * @throws IOException if no temporary directory could be determined
1241 * @throws SecurityException if access to <code>java.io.tmpdir</code> is not allowed within the current security context
1242 *
1243 * @see PropertyAccess#getProperty(String, boolean)
1244 * @see Context#getDir(String, int)
1245 */
1246 public static File getTempDir(final boolean executable)
1247 throws SecurityException, IOException
1248 {
1249 if(!tempRootSet) { // volatile: ok
1250 synchronized(IOUtil.class) {
1251 if(!tempRootSet) {
1252 tempRootSet = true;
1253 {
1254 final File ctxTempDir = AndroidUtils.getTempRoot(); // null if ( !Android || no android-ctx )
1255 if(null != ctxTempDir) {
1256 tempRootNoexec = getSubTempDir(ctxTempDir, tmpSubDir, false /* executable, see below */, "Android.ctxTemp");
1257 tempRootExec = tempRootNoexec; // FIXME: Android temp root is always executable (?)
1258 return tempRootExec;
1259 }
1260 }
1261
1262 final File java_io_tmpdir = getFile( PropertyAccess.getProperty(java_io_tmpdir_propkey, false) );
1263 if(DEBUG) {
1264 System.err.println("IOUtil.getTempRoot(): tempX1 <"+java_io_tmpdir+">, used "+(null!=java_io_tmpdir));
1265 }
1266
1267 final File user_tmpdir; // only if diff than java_io_tmpdir
1268 {
1269 String __user_tmpdir = System.getenv("TMPDIR");
1270 if( !isStringSet(__user_tmpdir) ) {
1271 __user_tmpdir = System.getenv("TEMP");
1272 }
1273 final File _user_tmpdir = getFile(__user_tmpdir);
1274 if( null != _user_tmpdir && !_user_tmpdir.equals(java_io_tmpdir) ) {
1275 user_tmpdir = _user_tmpdir;
1276 } else {
1277 user_tmpdir = null;
1278 }
1279 if(DEBUG) {
1280 System.err.println("IOUtil.getTempRoot(): tempX3 <"+_user_tmpdir+">, used "+(null!=user_tmpdir));
1281 }
1282 }
1283
1284 final File user_home = getFile( PropertyAccess.getProperty(user_home_propkey, false) );
1285 if(DEBUG) {
1286 System.err.println("IOUtil.getTempRoot(): tempX4 <"+user_home+">, used "+(null!=user_home));
1287 }
1288
1289 final File xdg_cache_home;
1290 {
1291 String __xdg_cache_home;
1292 if( getOSHasFreeDesktopXDG() ) {
1293 __xdg_cache_home = System.getenv(XDG_CACHE_HOME_envkey);
1294 if( !isStringSet(__xdg_cache_home) && null != user_home ) {
1295 __xdg_cache_home = user_home.getAbsolutePath() + File.separator + ".cache" ; // default
1296 }
1297 } else {
1298 __xdg_cache_home = null;
1299 }
1300 final File _xdg_cache_home = getFile(__xdg_cache_home);
1301 if( null != _xdg_cache_home && !_xdg_cache_home.equals(java_io_tmpdir) ) {
1302 xdg_cache_home = _xdg_cache_home;
1303 } else {
1304 xdg_cache_home = null;
1305 }
1306 if(DEBUG) {
1307 System.err.println("IOUtil.getTempRoot(): tempX2 <"+_xdg_cache_home+">, used "+(null!=xdg_cache_home));
1308 }
1309 }
1310
1311 // 1) java.io.tmpdir/jogamp
1312 if( null == tempRootExec && null != java_io_tmpdir ) {
1313 if( Platform.OSType.MACOS == PlatformPropsImpl.OS_TYPE || Platform.OSType.IOS == PlatformPropsImpl.OS_TYPE ) {
1314 // Bug 865: Safari >= 6.1 [OSX] May employ xattr on 'com.apple.quarantine' on 'PluginProcess.app'
1315 // We attempt to fix this issue _after_ gluegen native lib is loaded, see JarUtil.fixNativeLibAttribs(File).
1316 tempRootExec = getSubTempDir(java_io_tmpdir, tmpSubDir, false /* executable */, "tempX1");
1317 } else {
1318 tempRootExec = getSubTempDir(java_io_tmpdir, tmpSubDir, true /* executable */, "tempX1");
1319 }
1320 }
1321
1322 // 2) $XDG_CACHE_HOME/jogamp
1323 if( null == tempRootExec && null != xdg_cache_home ) {
1324 tempRootExec = getSubTempDir(xdg_cache_home, tmpSubDir, true /* executable */, "tempX2");
1325 }
1326
1327 // 3) $TMPDIR/jogamp
1328 if( null == tempRootExec && null != user_tmpdir ) {
1329 tempRootExec = getSubTempDir(user_tmpdir, tmpSubDir, true /* executable */, "tempX3");
1330 }
1331
1332 // 4) $HOME/.jogamp
1333 if( null == tempRootExec && null != user_home ) {
1334 tempRootExec = getSubTempDir(user_home, "." + tmpSubDir, true /* executable */, "tempX4");
1335 }
1336
1337 if( null != tempRootExec ) {
1338 tempRootNoexec = tempRootExec;
1339 } else {
1340 // 1) java.io.tmpdir/jogamp
1341 if( null == tempRootNoexec && null != java_io_tmpdir ) {
1342 tempRootNoexec = getSubTempDir(java_io_tmpdir, tmpSubDir, false /* executable */, "temp01");
1343 }
1344
1345 // 2) $XDG_CACHE_HOME/jogamp
1346 if( null == tempRootNoexec && null != xdg_cache_home ) {
1347 tempRootNoexec = getSubTempDir(xdg_cache_home, tmpSubDir, false /* executable */, "temp02");
1348 }
1349
1350 // 3) $TMPDIR/jogamp
1351 if( null == tempRootNoexec && null != user_tmpdir ) {
1352 tempRootNoexec = getSubTempDir(user_tmpdir, tmpSubDir, false /* executable */, "temp03");
1353 }
1354
1355 // 4) $HOME/.jogamp
1356 if( null == tempRootNoexec && null != user_home ) {
1357 tempRootNoexec = getSubTempDir(user_home, "." + tmpSubDir, false /* executable */, "temp04");
1358 }
1359 }
1360
1361 if(DEBUG) {
1362 final String tempRootExecAbsPath = null != tempRootExec ? tempRootExec.getAbsolutePath() : null;
1363 final String tempRootNoexecAbsPath = null != tempRootNoexec ? tempRootNoexec.getAbsolutePath() : null;
1364 System.err.println("IOUtil.getTempRoot(): temp dirs: exec: "+tempRootExecAbsPath+", noexec: "+tempRootNoexecAbsPath);
1365 }
1366 }
1367 }
1368 }
1369 final File r = executable ? tempRootExec : tempRootNoexec ;
1370 if(null == r) {
1371 final String exe_s = executable ? "executable " : "";
1372 throw new IOException("Could not determine a temporary "+exe_s+"directory");
1373 }
1374 final FilePermission fp = new FilePermission(r.getAbsolutePath(), "read,write,delete");
1376 return r;
1377 }
1378 private static File tempRootExec = null; // writeable and executable
1379 private static File tempRootNoexec = null; // writeable, maybe executable
1380 private static volatile boolean tempRootSet = false;
1381
1382 /**
1383 * Utilizing {@link File#createTempFile(String, String, File)} using
1384 * {@link #getTempDir(boolean)} as the directory parameter, ie. location
1385 * of the root temp folder.
1386 *
1387 * @see File#createTempFile(String, String)
1388 * @see File#createTempFile(String, String, File)
1389 * @see #getTempDir(boolean)
1390 *
1391 * @param prefix
1392 * @param suffix
1393 * @param executable true if the temporary root folder needs to hold executable files, otherwise false.
1394 * @return
1395 * @throws IllegalArgumentException
1396 * @throws IOException if no temporary directory could be determined or temp file could not be created
1397 * @throws SecurityException
1398 */
1399 public static File createTempFile(final String prefix, final String suffix, final boolean executable)
1400 throws IllegalArgumentException, IOException, SecurityException
1401 {
1402 return File.createTempFile( prefix, suffix, getTempDir(executable) );
1403 }
1404
1405 public static void close(final Closeable stream, final boolean throwRuntimeException) throws RuntimeException {
1406 if(null != stream) {
1407 try {
1408 stream.close();
1409 } catch (final IOException e) {
1410 if(throwRuntimeException) {
1411 throw new RuntimeException(e);
1412 } else if(DEBUG) {
1413 System.err.println("Caught Exception: ");
1414 e.printStackTrace();
1415 }
1416 }
1417 }
1418 }
1419
1420 /**
1421 * Helper to simplify closing {@link Closeable}s.
1422 *
1423 * @param stream the {@link Closeable} instance to close
1424 * @param saveOneIfFree cache for one {@link IOException} to store, if not already used (excess)
1425 * @param dumpExcess dump the excess {@link IOException} on this {@link PrintStream}
1426 * @return the excess {@link IOException} or {@code null}.
1427 */
1428 public static IOException close(final Closeable stream, final IOException[] saveOneIfFree, final PrintStream dumpExcess) {
1429 try {
1430 stream.close();
1431 } catch(final IOException e) {
1432 if( null == saveOneIfFree[0] ) {
1433 saveOneIfFree[0] = e;
1434 } else {
1435 if( null != dumpExcess ) {
1436 dumpExcess.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
1437 e.printStackTrace(dumpExcess);
1438 }
1439 return e;
1440 }
1441 }
1442 return null;
1443 }
1444
1445 /**
1446 * Retrieve the list of all filenames traversing through given paths
1447 * @param paths list of paths to traverse through, containing directories and files
1448 * @param excludes optional list of exclude {@link Pattern}. All {@link Pattern#matcher(CharSequence) matching} files or directories will be omitted. Maybe be null or empty.
1449 * @param includes optional list of explicit include {@link Pattern}. If given, only {@link Pattern#matcher(CharSequence) matching} files will be returned, otherwise all occurring.
1450 * @return list of unsorted filenames within given paths
1451 */
1452 public static ArrayList<String> filesOf(final List<String> paths, final List<Pattern> excludes, final List<Pattern> includes) {
1453 final ArrayList<String> files = new ArrayList<String>(paths.size()*32);
1454 final ArrayList<String> todo = new ArrayList<String>(paths);
1455 while(todo.size() > 0) {
1456 final String p = todo.remove(0);
1457 if( null != excludes && excludes.size() > 0) {
1458 boolean exclude = false;
1459 for(int i=0; !exclude && i<excludes.size(); i++) {
1460 exclude = excludes.get(i).matcher(p).matches();
1461 if( DEBUG ) {
1462 if( exclude ) {
1463 System.err.println("IOUtil.filesOf(): excluding <"+p+"> (exclude["+i+"]: "+excludes.get(i)+")");
1464 }
1465 }
1466 }
1467 if( exclude ) {
1468 continue; // skip further processing, continue w/ next path
1469 }
1470 }
1471 final File f = new File(p);
1472 if( !f.exists() ) {
1473 if( DEBUG ) {
1474 System.err.println("IOUtil.filesOf(): not existing: "+f);
1475 }
1476 continue;
1477 } else if( f.isDirectory() ) {
1478 final String[] subs = f.list();
1479 if( null == subs ) {
1480 if( DEBUG ) {
1481 System.err.println("IOUtil.filesOf(): null list of directory: "+f);
1482 }
1483 } else if( 0 == subs.length ) {
1484 if( DEBUG ) {
1485 System.err.println("IOUtil.filesOf(): empty list of directory: "+f);
1486 }
1487 } else {
1488 int j=0;
1489 final String pp = p.endsWith("/") ? p : p+"/";
1490 for(int i=0; i<subs.length; i++) {
1491 todo.add(j++, pp+subs[i]); // add 'in-place' to soothe the later sorting algorithm
1492 }
1493 }
1494 } else {
1495 if( null != includes && includes.size() > 0) {
1496 boolean include = false;
1497 for(int i=0; !include && i<includes.size(); i++) {
1498 include = includes.get(i).matcher(p).matches();
1499 if( DEBUG ) {
1500 if( include ) {
1501 System.err.println("IOUtil.filesOf(): including <"+p+"> (including["+i+"]: "+includes.get(i)+")");
1502 }
1503 }
1504 }
1505 if( include ) {
1506 files.add(p);
1507 }
1508 } else {
1509 files.add(p);
1510 }
1511 }
1512 }
1513 return files;
1514 }
1515}
static void dumpThrowable(final String additionalDescr, final Throwable t)
Dumps a Throwable to System.err in a decorating message including the current thread name,...
A generic unchecked exception for Jogamp errors used throughout the binding as a substitute for Runti...
See PiggybackURLConnection for description and examples.
static final String asset_protocol_prefix
The asset URL protocol prefix asset:
static URL createURL(final String path, final ClassLoader cl)
Create an asset URL, suitable even w/o the registered asset URLStreamHandler.
URLConnection resolve(final String path)
Resolving path to a URL sub protocol and return it's open URLConnection.
final String get()
Returns the encoded String.
Definition: Uri.java:336
This class implements an immutable Uri as defined by RFC 2396.
Definition: Uri.java:160
ASCIIEncoded toASCIIString()
Returns the encoded input encoded in US-ASCII.
Definition: Uri.java:1308
final boolean isFileScheme()
Returns true, if this instance is a file scheme, otherwise false.
Definition: Uri.java:1255
final File toFile()
If this instance is a file scheme, implementation decodes [ "//"+authority ] + path,...
Definition: Uri.java:1390
Utility methods allowing easy java.nio.Buffer manipulations.
Definition: Buffers.java:70
static ByteBuffer newDirectByteBuffer(final int numElements)
Allocates a new direct ByteBuffer with the specified number of elements.
Definition: Buffers.java:92
Machine data description for alignment and size onle, see com.jogamp.gluegen.
Utility class for querying platform specific properties.
Definition: Platform.java:58
static MachineDataInfo getMachineDataInfo()
Returns the MachineDataInfo of the running machine.
Definition: Platform.java:510
Helper compound associating a class instance and resource paths to be resolved at a later time.
Definition: IOUtil.java:518
final int resourceCount()
Returns the number of resources, i.e.
Definition: IOUtil.java:529
final String[] resourcePaths
Resource paths, see resolve(int).
Definition: IOUtil.java:526
URLConnection resolve(final int uriIndex)
Resolving one of the resourcePaths indexed by uriIndex using classLoader, contextCL through IOUtil#ge...
Definition: IOUtil.java:552
final ClassLoader classLoader
Optional ClassLoader used to resolve(int) resourcePaths.
Definition: IOUtil.java:520
ClassResources(final String[] resourcePaths, final ClassLoader classLoader, final Class<?> relContext)
Definition: IOUtil.java:536
final Class<?> contextCL
Optional class instance used to resolve(int) relative resourcePaths.
Definition: IOUtil.java:523
StreamMonitor(final InputStream[] streams, final PrintStream ostream, final String prefix)
Definition: IOUtil.java:945
static FileOutputStream getFileOutputStream(final File file, final boolean allowOverwrite)
Definition: IOUtil.java:448
static String cleanPathString(String path)
Definition: IOUtil.java:712
static int copyStream2Stream(final int bufferSize, final InputStream in, final OutputStream out)
Copy the complete specified input stream to the specified output stream.
Definition: IOUtil.java:236
static ByteBuffer copyStream2ByteBuffer(InputStream stream, int initialCapacity)
Copy the complete specified input stream to a NIO ByteBuffer w/ native byte order,...
Definition: IOUtil.java:307
static File createTempFile(final String prefix, final String suffix, final boolean executable)
Utilizing File#createTempFile(String, String, File) using getTempDir(boolean) as the directory parame...
Definition: IOUtil.java:1399
static String getBasename(String fname)
Returns the basename of the given fname w/o directory part.
Definition: IOUtil.java:486
static final String tmpSubDir
Subdirectory within platform's temporary root directory where all JogAmp related temp files are being...
Definition: IOUtil.java:132
static URL getClassURL(final String clazzBinName, final ClassLoader cl)
Definition: IOUtil.java:474
static URLConnection openURL(final URL url, final String dbgmsg)
Returns the connected URLConnection, or null if not url is not available.
Definition: IOUtil.java:762
static URLConnection getResource(final String resourcePath, final ClassLoader cl)
Locating a resource using the ClassLoader's facilities.
Definition: IOUtil.java:617
static final boolean DEBUG
Definition: IOUtil.java:70
static boolean testFile(final File file, final boolean shallBeDir, final boolean shallBeWritable)
Test whether file exists and matches the given requirements.
Definition: IOUtil.java:918
static URLConnection getResource(final String resourcePath, final ClassLoader classLoader, final Class<?> relContext)
Locating a resource using getResource(String, ClassLoader):
Definition: IOUtil.java:578
static String getRelativeOf(final File baseLocation, final String relativeFile)
Generates a path for the 'relativeFile' relative to the 'baseLocation'.
Definition: IOUtil.java:652
static File getTempDir(final boolean executable)
Returns a platform independent writable directory for temporary files consisting of the platform's te...
Definition: IOUtil.java:1246
static String getParentOf(final String path)
Definition: IOUtil.java:670
static int copyStream2Stream(final InputStream in, final OutputStream out)
Copy the complete specified input stream to the specified output stream.
Definition: IOUtil.java:222
static String getFileSuffix(final File file)
Returns the lowercase suffix of the given file name (the text after the last '.
Definition: IOUtil.java:411
static String slashify(final String path, final boolean startWithSlash, final boolean endWithSlash)
Definition: IOUtil.java:389
static URLConnection openURL(final URL url)
Returns the connected URLConnection, or null if not url is not available.
Definition: IOUtil.java:755
static ByteBuffer copyStream2ByteBuffer(final InputStream stream)
Copy the complete specified input stream to a NIO ByteBuffer w/ native byte order,...
Definition: IOUtil.java:297
static void close(final Closeable stream, final boolean throwRuntimeException)
Definition: IOUtil.java:1405
static String getClassFileName(final String clazzBinName)
Definition: IOUtil.java:463
static String getDirname(String fname)
Returns unified '/' dirname including the last '/'.
Definition: IOUtil.java:499
static ByteBuffer copyStreamChunk2ByteBuffer(InputStream stream, int skipBytes, int byteCount)
Copy the specified input stream chunk to a NIO ByteBuffer w/ native byte order, which is being return...
Definition: IOUtil.java:347
static String getFileSuffix(final String filename)
Returns the lowercase suffix of the given file name (the text after the last '.
Definition: IOUtil.java:425
static boolean testDirExec(final File dir)
Returns true if the given dir @endiliteral.
Definition: IOUtil.java:1018
static final Pattern patternSpaceEnc
Definition: IOUtil.java:735
static File testDir(final File dir, final boolean create, final boolean executable)
Returns the directory dir, which is processed and tested as described below.
Definition: IOUtil.java:1167
static ArrayList< String > filesOf(final List< String > paths, final List< Pattern > excludes, final List< Pattern > includes)
Retrieve the list of all filenames traversing through given paths.
Definition: IOUtil.java:1452
static String getUriFilePathOrASCII(final Uri uri)
If uri is a file scheme implementation returns Uri#toFile().
Definition: IOUtil.java:744
static int copyURLConn2File(final URLConnection conn, final File outFile)
Copy the complete specified URL resource to the specified output file.
Definition: IOUtil.java:182
static byte[] copyStream2ByteArray(InputStream stream)
Copy the complete specified input stream to a byte array, which is being returned.
Definition: IOUtil.java:262
static StringBuilder appendCharStream(final StringBuilder sb, final Reader r)
Definition: IOUtil.java:250
static int copyStream2File(final InputStream in, final File outFile)
Copy the complete specified input stream to the specified output file.
Definition: IOUtil.java:204
static IOException close(final Closeable stream, final IOException[] saveOneIfFree, final PrintStream dumpExcess)
Helper to simplify closing Closeables.
Definition: IOUtil.java:1428
java.lang.Thread specialization implementing InterruptSource to track java.lang.Thread#interrupt() ca...
Helper routines for accessing properties.
static final boolean getBooleanProperty(final String property, final boolean jnlpAlias)
static final boolean isPropertyDefined(final String property, final boolean jnlpAlias)
static final String getProperty(final String propertyKey, final boolean jnlpAlias)
Query the property with the name propertyKey.
static final Constructor<?> getConstructor(final String clazzName, final Class<?>[] cstrArgTypes, final boolean initializeClazz, final ClassLoader cl)
static final Class<?> getClass(final String clazzName, final boolean initializeClazz, final ClassLoader cl)
Loads and returns the class or null.
static< T > T doPrivileged(final PrivilegedAction< T > o)
Call wrapper for java.security.AccessController#doPrivileged(PrivilegedAction).
static final void checkPermission(final Permission perm)
Throws an SecurityException if an installed SecurityManager does not permit the requested Permission.
Interface exposing java.lang.Thread#interrupt() source, intended for java.lang.Thread specializations...