GlueGen v2.6.0
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 getExeTestShellCodeUnix(final String test) {
796 if( testExeFile(new File(test)) ) {
797 return "#!"+test+PlatformPropsImpl.NEWLINE;
798 }
799 return null;
800 }
801 private static String getExeTestShellCode() {
802 if( PlatformPropsImpl.OS_TYPE == Platform.OSType.WINDOWS ) {
803 return "echo off"+PlatformPropsImpl.NEWLINE;
804 }
805 final String res = getExeTestShellCodeUnix("/usr/bin/true");
806 if( null != res ) {
807 return res;
808 }
809 return getExeTestShellCodeUnix("/bin/true");
810 }
811 private static String getExeNativePath(final String canonicalPath) {
812 switch(PlatformPropsImpl.OS_TYPE) {
813 case WINDOWS:
814 return "\""+canonicalPath+"\"";
815 default:
816 return canonicalPath;
817 }
818 }
819 private static String[] getExeTestCommandArgs(final String scriptFile) {
820 switch(PlatformPropsImpl.OS_TYPE) {
821 case WINDOWS:
822 // return new String[] { "cmd", "/c", scriptFile };
823 default:
824 return new String[] { scriptFile };
825 }
826 }
827
828 private static final byte[] readCode(final String fname) throws IOException {
829 final URLConnection con = IOUtil.getResource(fname, IOUtil.class.getClassLoader(), IOUtil.class);
830 final InputStream in = con.getInputStream();
831 byte[] output = null;
832 try {
833 output = CustomCompress.inflateFromStream(in);
834 } finally {
835 in.close();
836 }
837 return output;
838 }
839 private static final Object exeTestLock = new Object();
840 private static WeakReference<byte[]> exeTestCodeRef = null;
841
842 private static void fillExeTestFile(final File exefile) throws IOException {
843 if( useNativeExeFile &&
844 Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE &&
845 Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family
846 ) {
847 final byte[] exeTestCode;
848 synchronized ( exeTestLock ) {
849 byte[] _exeTestCode = null;
850 if( null == exeTestCodeRef || null == ( _exeTestCode = exeTestCodeRef.get() ) ) {
851 final String fname;
852 if( Platform.CPUType.X86_64 == PlatformPropsImpl.CPU_ARCH ) {
853 fname = "bin/exe-windows-x86_64.defl";
854 } else {
855 fname = "bin/exe-windows-i386.defl";
856 }
857 exeTestCode = readCode(fname);
858 exeTestCodeRef = new WeakReference<byte[]>(exeTestCode);
859 } else {
860 exeTestCode = _exeTestCode;
861 }
862 }
863 final FileOutputStream out = new FileOutputStream(exefile);
864 try {
865 out.write(exeTestCode, 0, exeTestCode.length);
866 try {
867 out.getFD().sync();
868 } catch (final SyncFailedException sfe) {
869 ExceptionUtils.dumpThrowable("", sfe);
870 }
871 } finally {
872 out.close();
873 }
874 } else {
875 final String shellCode = getExeTestShellCode();
876 if( isStringSet(shellCode) ) {
877 final FileWriter fout = new FileWriter(exefile);
878 try {
879 fout.write(shellCode);
880 try {
881 fout.flush();
882 } catch (final IOException sfe) {
883 ExceptionUtils.dumpThrowable("", sfe);
884 }
885 } finally {
886 fout.close();
887 }
888 }
889 }
890 }
891 private static boolean getOSHasNoexecFS() {
892 switch(PlatformPropsImpl.OS_TYPE) {
893 case OPENKODE:
894 return false;
895
896 default:
897 return true;
898 }
899 }
900
901 /**
902 * @see <a href="http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html">Free-Desktop - XDG Base Directory Specification</a>
903 */
904 private static boolean getOSHasFreeDesktopXDG() {
905 switch(PlatformPropsImpl.OS_TYPE) {
906 case ANDROID:
907 case MACOS:
908 case IOS:
909 case WINDOWS:
910 case OPENKODE:
911 return false;
912
913 default:
914 return true;
915 }
916 }
917
918 /**
919 * Test whether {@code file} exists and matches the given requirements
920 *
921 * @param file
922 * @param shallBeDir
923 * @param shallBeWritable
924 * @return
925 */
926 public static boolean testFile(final File file, final boolean shallBeDir, final boolean shallBeWritable) {
927 if (!file.exists()) {
928 if(DEBUG) {
929 System.err.println("IOUtil.testFile: <"+file.getAbsolutePath()+">: does not exist");
930 }
931 return false;
932 }
933 if (shallBeDir && !file.isDirectory()) {
934 if(DEBUG) {
935 System.err.println("IOUtil.testFile: <"+file.getAbsolutePath()+">: is not a directory");
936 }
937 return false;
938 }
939 if (shallBeWritable && !file.canWrite()) {
940 if(DEBUG) {
941 System.err.println("IOUtil.testFile: <"+file.getAbsolutePath()+">: is not writable");
942 }
943 return false;
944 }
945 return true;
946 }
947 /**
948 * Test whether executable {@code file} exists, no OS executable tests performed, just permissions via Java
949 *
950 * @param file
951 * @return
952 */
953 public static boolean testExeFile(final File file) {
954 if (!file.exists()) {
955 if(DEBUG) {
956 System.err.println("IOUtil.testFile: <"+file.getAbsolutePath()+">: does not exist");
957 }
958 return false;
959 }
960 if (!file.canExecute()) {
961 if(DEBUG) {
962 System.err.println("IOUtil.testFile: <"+file.getAbsolutePath()+">: can't be executed");
963 }
964 return false;
965 }
966 return true;
967 }
968
969 public static class StreamMonitor implements Runnable {
970 private final InputStream[] istreams;
971 private final boolean[] eos;
972 private final PrintStream ostream;
973 private final String prefix;
974 public StreamMonitor(final InputStream[] streams, final PrintStream ostream, final String prefix) {
975 this.istreams = streams;
976 this.eos = new boolean[streams.length];
977 this.ostream = ostream;
978 this.prefix = prefix;
979 final InterruptSource.Thread t = new InterruptSource.Thread(null, this, "StreamMonitor-"+Thread.currentThread().getName());
980 t.setDaemon(true);
981 t.start();
982 }
983
984 @Override
985 public void run()
986 {
987 final byte[] buffer = new byte[4096];
988 try {
989 final int streamCount = istreams.length;
990 int eosCount = 0;
991 do {
992 for(int i=0; i<istreams.length; i++) {
993 if( !eos[i] ) {
994 final int numReadI = istreams[i].read(buffer);
995 if (numReadI > 0) {
996 if( null != ostream ) {
997 if( null != prefix ) {
998 ostream.write(prefix.getBytes());
999 }
1000 ostream.write(buffer, 0, numReadI);
1001 }
1002 } else {
1003 // numReadI == -1
1004 eosCount++;
1005 eos[i] = true;
1006 }
1007 }
1008 }
1009 if( null != ostream ) {
1010 ostream.flush();
1011 }
1012 } while ( eosCount < streamCount );
1013 } catch (final IOException e) {
1014 } finally {
1015 if( null != ostream ) {
1016 ostream.flush();
1017 }
1018 // Should allow clean exit when process shuts down
1019 }
1020 }
1021 }
1022
1023 private static final Boolean isNioExecutableFile(final File file) {
1024 if( null != fileToPathGetter && null != isExecutableQuery ) {
1025 try {
1026 return (Boolean) isExecutableQuery.invoke(null, fileToPathGetter.invoke(file));
1027 } catch (final Throwable t) {
1028 throw new JogampRuntimeException("error invoking Files.isExecutable(file.toPath())", t);
1029 }
1030 } else {
1031 return null;
1032 }
1033 }
1034
1035 /**
1036 * Returns true if the given {@code dir}
1037 * <ol>
1038 * <li>exists, and</li>
1039 * <li>is a directory, and</li>
1040 * <li>is writeable, and</li>
1041 * <li>files can be executed from the directory</li>
1042 * </ol>
1043 *
1044 * @throws SecurityException if file creation and process execution is not allowed within the current security context
1045 * @param dir
1046 */
1047 public static boolean testDirExec(final File dir)
1048 throws SecurityException
1049 {
1050 final boolean debug = DEBUG_EXE || DEBUG;
1051
1052 if( !testTempDirExec ) {
1053 if(DEBUG) {
1054 System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Disabled TestTempDirExec");
1055 }
1056 return false;
1057 }
1058 if (!testFile(dir, true, true)) {
1059 if( debug ) {
1060 System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Not writeable dir");
1061 }
1062 return false;
1063 }
1064 if(!getOSHasNoexecFS()) {
1065 if( debug ) {
1066 System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Always executable");
1067 }
1068 return true;
1069 }
1070
1071 final long t0 = debug ? System.currentTimeMillis() : 0;
1072 final File exeTestFile;
1073 String exeNativePath;
1074 final boolean existingExe;
1075 try {
1076 final File permExeTestFile = DEBUG_EXE_EXISTING_FILE ? new File(dir, "jogamp_exe_tst"+getExeTestFileSuffix()) : null;
1077 if( null != permExeTestFile && permExeTestFile.exists() ) {
1078 exeTestFile = permExeTestFile;
1079 existingExe = true;
1080 } else {
1081 exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir);
1082 existingExe = false;
1083 fillExeTestFile(exeTestFile);
1084 }
1085 exeNativePath = getExeNativePath( exeTestFile.getCanonicalPath() );
1086 } catch (final SecurityException se) {
1087 throw se; // fwd Security exception
1088 } catch (final IOException e) {
1089 if( debug ) {
1090 e.printStackTrace();
1091 }
1092 return false;
1093 }
1094 final long t1 = debug ? System.currentTimeMillis() : 0;
1095 long t2;
1096 int res = -1;
1097 int exitValue = -1;
1098 Boolean isNioExec = null;
1099 if( existingExe || exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */) ) {
1100 t2 = debug ? System.currentTimeMillis() : 0;
1101 // First soft exec test via NIO's ACL check, if available
1102 isNioExec = isNioExecutableFile(exeTestFile);
1103 if( null != isNioExec ) {
1104 res = isNioExec.booleanValue() ? 0 : -1;
1105 }
1106 if( null == isNioExec || 0 <= res ) {
1107 // Hard exec test via actual execution, if NIO's ACL check succeeded or not available.
1108 // Required, since Windows 'Software Restriction Policies (SRP)' won't be triggered merely by NIO's ACL check.
1109 Process pr = null;
1110 try {
1111 // Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)'
1112 // and hence splitting up command by spaces!
1113 // Note: All no-exec cases throw an IOExceptions at ProcessBuilder.start(), i.e. below exec() call!
1114 pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeNativePath ), null, null );
1115 if( DEBUG_EXE && !DEBUG_EXE_NOSTREAM ) {
1116 new StreamMonitor(new InputStream[] { pr.getInputStream(), pr.getErrorStream() }, System.err, "Exe-Tst: ");
1117 }
1118 pr.waitFor();
1119 exitValue = pr.exitValue(); // Note: Bug 1219 Comment 50: On reporter's machine exit value 1 is being returned
1120 if( 0 == exitValue ) {
1121 res++; // file has been executed and exited normally
1122 } else {
1123 res = -2; // abnormal termination
1124 }
1125 } catch (final SecurityException se) {
1126 throw se; // fwd Security exception
1127 } catch (final Throwable t) {
1128 t2 = debug ? System.currentTimeMillis() : 0;
1129 res = -3;
1130 if( debug ) {
1131 System.err.println("IOUtil.testDirExec: <"+exeNativePath+">: Caught "+t.getClass().getSimpleName()+": "+t.getMessage());
1132 t.printStackTrace();
1133 }
1134 } finally {
1135 if( null != pr ) {
1136 // Bug 1219 Comment 58: Ensure that the launched process gets terminated!
1137 // This is Process implementation specific and varies on different platforms,
1138 // hence it may be required.
1139 try {
1140 pr.destroy();
1141 } catch (final Throwable t) {
1143 }
1144 }
1145 }
1146 }
1147 } else {
1148 t2 = debug ? System.currentTimeMillis() : 0;
1149 }
1150
1151 final boolean ok = 0 <= res;
1152 if( !DEBUG_EXE && !existingExe ) {
1153 exeTestFile.delete();
1154 }
1155 if( debug ) {
1156 final long t3 = System.currentTimeMillis();
1157 System.err.println("IOUtil.testDirExec(): test-exe <"+exeNativePath+">, existingFile "+existingExe+", isNioExec "+isNioExec+", returned "+exitValue);
1158 System.err.println("IOUtil.testDirExec(): abs-path <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok);
1159 System.err.println("IOUtil.testDirExec(): total "+(t3-t0)+"ms, create "+(t1-t0)+"ms, fill "+(t2-t1)+"ms, execute "+(t3-t2)+"ms");
1160 }
1161 return ok;
1162 }
1163
1164 private static File testDirImpl(final File dir, final boolean create, final boolean executable, final String dbgMsg)
1165 throws SecurityException
1166 {
1167 final File res;
1168 if (create && !dir.exists()) {
1169 dir.mkdirs();
1170 }
1171 if( executable ) {
1172 res = testDirExec(dir) ? dir : null;
1173 } else {
1174 res = testFile(dir, true, true) ? dir : null;
1175 }
1176 if(DEBUG) {
1177 System.err.println("IOUtil.testDirImpl("+dbgMsg+"): <"+dir.getAbsolutePath()+">, create "+create+", exec "+executable+": "+(null != res));
1178 }
1179 return res;
1180 }
1181
1182 /**
1183 * Returns the directory {@code dir}, which is processed and tested as described below.
1184 * <ol>
1185 * <li>If {@code create} is {@code true} and the directory does not exist yet, it is created incl. all sub-directories.</li>
1186 * <li>If {@code dirName} exists, but is not a directory, {@code null} is being returned.</li>
1187 * <li>If the directory does not exist or is not writeable, {@code null} is being returned.</li>
1188 * <li>If {@code executable} is {@code true} and files cannot be executed from the directory, {@code null} is being returned.</li>
1189 * </ol>
1190 *
1191 * @param dir the directory to process
1192 * @param create true if the directory shall be created if not existing
1193 * @param executable true if the user intents to launch executables from the temporary directory, otherwise false.
1194 * @throws SecurityException if file creation and process execution is not allowed within the current security context
1195 */
1196 public static File testDir(final File dir, final boolean create, final boolean executable)
1197 throws SecurityException
1198 {
1199 return testDirImpl(dir, create, executable, "testDir");
1200 }
1201
1202 private static boolean isStringSet(final String s) { return null != s && 0 < s.length(); }
1203
1204 /**
1205 * This methods finds [and creates] an available temporary sub-directory:
1206 * <pre>
1207 File tmpBaseDir = null;
1208 if(null != testDir(tmpRoot, true, executable)) { // check tmpRoot first
1209 for(int i = 0; null == tmpBaseDir && i<=9999; i++) {
1210 final String tmpDirSuffix = String.format("_%04d", i); // 4 digits for iteration
1211 tmpBaseDir = testDir(new File(tmpRoot, tmpSubDirPrefix+tmpDirSuffix), true, executable);
1212 }
1213 } else {
1214 tmpBaseDir = null;
1215 }
1216 return tmpBaseDir;
1217 * </pre>
1218 * <p>
1219 * The iteration through [0000-9999] ensures that the code is multi-user save.
1220 * </p>
1221 * @param tmpRoot
1222 * @param executable
1223 * @param dbgMsg
1224 * @param tmpDirPrefix
1225 * @return a temporary directory, writable by this user
1226 * @throws SecurityException
1227 */
1228 private static File getSubTempDir(final File tmpRoot, final String tmpSubDirPrefix, final boolean executable, final String dbgMsg)
1229 throws SecurityException
1230 {
1231 File tmpBaseDir = null;
1232 if(null != testDirImpl(tmpRoot, true /* create */, executable, dbgMsg)) { // check tmpRoot first
1233 for(int i = 0; null == tmpBaseDir && i<=9999; i++) {
1234 final String tmpDirSuffix = String.format((Locale)null, "_%04d", i); // 4 digits for iteration
1235 tmpBaseDir = testDirImpl(new File(tmpRoot, tmpSubDirPrefix+tmpDirSuffix), true /* create */, executable, dbgMsg);
1236 }
1237 }
1238 return tmpBaseDir;
1239 }
1240
1241 private static File getFile(final String fname) {
1242 if( isStringSet(fname) ) {
1243 return new File(fname);
1244 } else {
1245 return null;
1246 }
1247
1248 }
1249 /**
1250 * Returns a platform independent writable directory for temporary files
1251 * consisting of the platform's {@code temp-root} + {@link #tmpSubDir},
1252 * e.g. {@code /tmp/jogamp_0000/}.
1253 * <p>
1254 * On standard Java the {@code temp-root} folder is specified by <code>java.io.tempdir</code>.
1255 * </p>
1256 * <p>
1257 * On Android the {@code temp-root} folder is relative to the applications local folder
1258 * (see {@link Context#getDir(String, int)}) is returned, if
1259 * the Android application/activity has registered it's Application Context
1260 * via {@link jogamp.common.os.android.StaticContext.StaticContext#init(Context, ClassLoader) StaticContext.init(..)}.
1261 * This allows using the temp folder w/o the need for <code>sdcard</code>
1262 * access, which would be the <code>java.io.tempdir</code> location on Android!
1263 * </p>
1264 * <p>
1265 * In case {@code temp-root} is the users home folder,
1266 * a dot is being prepended to {@link #tmpSubDir}, i.e.: {@code /home/user/.jogamp_0000/}.
1267 * </p>
1268 * @param executable true if the user intents to launch executables from the temporary directory, otherwise false.
1269 * @throws IOException if no temporary directory could be determined
1270 * @throws SecurityException if access to <code>java.io.tmpdir</code> is not allowed within the current security context
1271 *
1272 * @see PropertyAccess#getProperty(String, boolean)
1273 * @see Context#getDir(String, int)
1274 */
1275 public static File getTempDir(final boolean executable)
1276 throws SecurityException, IOException
1277 {
1278 if(!tempRootSet) { // volatile: ok
1279 synchronized(IOUtil.class) {
1280 if(!tempRootSet) {
1281 tempRootSet = true;
1282 {
1283 final File ctxTempDir = AndroidUtils.getTempRoot(); // null if ( !Android || no android-ctx )
1284 if(null != ctxTempDir) {
1285 tempRootNoexec = getSubTempDir(ctxTempDir, tmpSubDir, false /* executable, see below */, "Android.ctxTemp");
1286 tempRootExec = tempRootNoexec; // FIXME: Android temp root is always executable (?)
1287 return tempRootExec;
1288 }
1289 }
1290
1291 final File java_io_tmpdir = getFile( PropertyAccess.getProperty(java_io_tmpdir_propkey, false) );
1292 if(DEBUG) {
1293 System.err.println("IOUtil.getTempRoot(): tempX1 <"+java_io_tmpdir+">, used "+(null!=java_io_tmpdir));
1294 }
1295
1296 final File user_tmpdir; // only if diff than java_io_tmpdir
1297 {
1298 String __user_tmpdir = System.getenv("TMPDIR");
1299 if( !isStringSet(__user_tmpdir) ) {
1300 __user_tmpdir = System.getenv("TEMP");
1301 }
1302 final File _user_tmpdir = getFile(__user_tmpdir);
1303 if( null != _user_tmpdir && !_user_tmpdir.equals(java_io_tmpdir) ) {
1304 user_tmpdir = _user_tmpdir;
1305 } else {
1306 user_tmpdir = null;
1307 }
1308 if(DEBUG) {
1309 System.err.println("IOUtil.getTempRoot(): tempX3 <"+_user_tmpdir+">, used "+(null!=user_tmpdir));
1310 }
1311 }
1312
1313 final File user_home = getFile( PropertyAccess.getProperty(user_home_propkey, false) );
1314 if(DEBUG) {
1315 System.err.println("IOUtil.getTempRoot(): tempX4 <"+user_home+">, used "+(null!=user_home));
1316 }
1317
1318 final File xdg_cache_home;
1319 {
1320 String __xdg_cache_home;
1321 if( getOSHasFreeDesktopXDG() ) {
1322 __xdg_cache_home = System.getenv(XDG_CACHE_HOME_envkey);
1323 if( !isStringSet(__xdg_cache_home) && null != user_home ) {
1324 __xdg_cache_home = user_home.getAbsolutePath() + File.separator + ".cache" ; // default
1325 }
1326 } else {
1327 __xdg_cache_home = null;
1328 }
1329 final File _xdg_cache_home = getFile(__xdg_cache_home);
1330 if( null != _xdg_cache_home && !_xdg_cache_home.equals(java_io_tmpdir) ) {
1331 xdg_cache_home = _xdg_cache_home;
1332 } else {
1333 xdg_cache_home = null;
1334 }
1335 if(DEBUG) {
1336 System.err.println("IOUtil.getTempRoot(): tempX2 <"+_xdg_cache_home+">, used "+(null!=xdg_cache_home));
1337 }
1338 }
1339
1340 // 1) java.io.tmpdir/jogamp
1341 if( null == tempRootExec && null != java_io_tmpdir ) {
1342 if( Platform.OSType.MACOS == PlatformPropsImpl.OS_TYPE || Platform.OSType.IOS == PlatformPropsImpl.OS_TYPE ) {
1343 // Bug 865: Safari >= 6.1 [OSX] May employ xattr on 'com.apple.quarantine' on 'PluginProcess.app'
1344 // We attempt to fix this issue _after_ gluegen native lib is loaded, see JarUtil.fixNativeLibAttribs(File).
1345 tempRootExec = getSubTempDir(java_io_tmpdir, tmpSubDir, false /* executable */, "tempX1");
1346 } else {
1347 tempRootExec = getSubTempDir(java_io_tmpdir, tmpSubDir, true /* executable */, "tempX1");
1348 }
1349 }
1350
1351 // 2) $XDG_CACHE_HOME/jogamp
1352 if( null == tempRootExec && null != xdg_cache_home ) {
1353 tempRootExec = getSubTempDir(xdg_cache_home, tmpSubDir, true /* executable */, "tempX2");
1354 }
1355
1356 // 3) $TMPDIR/jogamp
1357 if( null == tempRootExec && null != user_tmpdir ) {
1358 tempRootExec = getSubTempDir(user_tmpdir, tmpSubDir, true /* executable */, "tempX3");
1359 }
1360
1361 // 4) $HOME/.jogamp
1362 if( null == tempRootExec && null != user_home ) {
1363 tempRootExec = getSubTempDir(user_home, "." + tmpSubDir, true /* executable */, "tempX4");
1364 }
1365
1366 if( null != tempRootExec ) {
1367 tempRootNoexec = tempRootExec;
1368 } else {
1369 // 1) java.io.tmpdir/jogamp
1370 if( null == tempRootNoexec && null != java_io_tmpdir ) {
1371 tempRootNoexec = getSubTempDir(java_io_tmpdir, tmpSubDir, false /* executable */, "temp01");
1372 }
1373
1374 // 2) $XDG_CACHE_HOME/jogamp
1375 if( null == tempRootNoexec && null != xdg_cache_home ) {
1376 tempRootNoexec = getSubTempDir(xdg_cache_home, tmpSubDir, false /* executable */, "temp02");
1377 }
1378
1379 // 3) $TMPDIR/jogamp
1380 if( null == tempRootNoexec && null != user_tmpdir ) {
1381 tempRootNoexec = getSubTempDir(user_tmpdir, tmpSubDir, false /* executable */, "temp03");
1382 }
1383
1384 // 4) $HOME/.jogamp
1385 if( null == tempRootNoexec && null != user_home ) {
1386 tempRootNoexec = getSubTempDir(user_home, "." + tmpSubDir, false /* executable */, "temp04");
1387 }
1388 }
1389
1390 if(DEBUG) {
1391 final String tempRootExecAbsPath = null != tempRootExec ? tempRootExec.getAbsolutePath() : null;
1392 final String tempRootNoexecAbsPath = null != tempRootNoexec ? tempRootNoexec.getAbsolutePath() : null;
1393 System.err.println("IOUtil.getTempRoot(): temp dirs: exec: "+tempRootExecAbsPath+", noexec: "+tempRootNoexecAbsPath);
1394 }
1395 }
1396 }
1397 }
1398 final File r = executable ? tempRootExec : tempRootNoexec ;
1399 if(null == r) {
1400 final String exe_s = executable ? "executable " : "";
1401 throw new IOException("Could not determine a temporary "+exe_s+"directory");
1402 }
1403 final FilePermission fp = new FilePermission(r.getAbsolutePath(), "read,write,delete");
1405 return r;
1406 }
1407 private static File tempRootExec = null; // writeable and executable
1408 private static File tempRootNoexec = null; // writeable, maybe executable
1409 private static volatile boolean tempRootSet = false;
1410
1411 /**
1412 * Utilizing {@link File#createTempFile(String, String, File)} using
1413 * {@link #getTempDir(boolean)} as the directory parameter, ie. location
1414 * of the root temp folder.
1415 *
1416 * @see File#createTempFile(String, String)
1417 * @see File#createTempFile(String, String, File)
1418 * @see #getTempDir(boolean)
1419 *
1420 * @param prefix
1421 * @param suffix
1422 * @param executable true if the temporary root folder needs to hold executable files, otherwise false.
1423 * @return
1424 * @throws IllegalArgumentException
1425 * @throws IOException if no temporary directory could be determined or temp file could not be created
1426 * @throws SecurityException
1427 */
1428 public static File createTempFile(final String prefix, final String suffix, final boolean executable)
1429 throws IllegalArgumentException, IOException, SecurityException
1430 {
1431 return File.createTempFile( prefix, suffix, getTempDir(executable) );
1432 }
1433
1434 public static void close(final Closeable stream, final boolean throwRuntimeException) throws RuntimeException {
1435 if(null != stream) {
1436 try {
1437 stream.close();
1438 } catch (final IOException e) {
1439 if(throwRuntimeException) {
1440 throw new RuntimeException(e);
1441 } else if(DEBUG) {
1442 System.err.println("Caught Exception: ");
1443 e.printStackTrace();
1444 }
1445 }
1446 }
1447 }
1448
1449 /**
1450 * Helper to simplify closing {@link Closeable}s.
1451 *
1452 * @param stream the {@link Closeable} instance to close
1453 * @param saveOneIfFree cache for one {@link IOException} to store, if not already used (excess)
1454 * @param dumpExcess dump the excess {@link IOException} on this {@link PrintStream}
1455 * @return the excess {@link IOException} or {@code null}.
1456 */
1457 public static IOException close(final Closeable stream, final IOException[] saveOneIfFree, final PrintStream dumpExcess) {
1458 try {
1459 stream.close();
1460 } catch(final IOException e) {
1461 if( null == saveOneIfFree[0] ) {
1462 saveOneIfFree[0] = e;
1463 } else {
1464 if( null != dumpExcess ) {
1465 dumpExcess.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
1466 e.printStackTrace(dumpExcess);
1467 }
1468 return e;
1469 }
1470 }
1471 return null;
1472 }
1473
1474 /**
1475 * Retrieve the list of all filenames traversing through given paths
1476 * @param paths list of paths to traverse through, containing directories and files
1477 * @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.
1478 * @param includes optional list of explicit include {@link Pattern}. If given, only {@link Pattern#matcher(CharSequence) matching} files will be returned, otherwise all occurring.
1479 * @return list of unsorted filenames within given paths
1480 */
1481 public static ArrayList<String> filesOf(final List<String> paths, final List<Pattern> excludes, final List<Pattern> includes) {
1482 final ArrayList<String> files = new ArrayList<String>(paths.size()*32);
1483 final ArrayList<String> todo = new ArrayList<String>(paths);
1484 while(todo.size() > 0) {
1485 final String p = todo.remove(0);
1486 if( null != excludes && excludes.size() > 0) {
1487 boolean exclude = false;
1488 for(int i=0; !exclude && i<excludes.size(); i++) {
1489 exclude = excludes.get(i).matcher(p).matches();
1490 if( DEBUG ) {
1491 if( exclude ) {
1492 System.err.println("IOUtil.filesOf(): excluding <"+p+"> (exclude["+i+"]: "+excludes.get(i)+")");
1493 }
1494 }
1495 }
1496 if( exclude ) {
1497 continue; // skip further processing, continue w/ next path
1498 }
1499 }
1500 final File f = new File(p);
1501 if( !f.exists() ) {
1502 if( DEBUG ) {
1503 System.err.println("IOUtil.filesOf(): not existing: "+f);
1504 }
1505 continue;
1506 } else if( f.isDirectory() ) {
1507 final String[] subs = f.list();
1508 if( null == subs ) {
1509 if( DEBUG ) {
1510 System.err.println("IOUtil.filesOf(): null list of directory: "+f);
1511 }
1512 } else if( 0 == subs.length ) {
1513 if( DEBUG ) {
1514 System.err.println("IOUtil.filesOf(): empty list of directory: "+f);
1515 }
1516 } else {
1517 int j=0;
1518 final String pp = p.endsWith("/") ? p : p+"/";
1519 for(int i=0; i<subs.length; i++) {
1520 todo.add(j++, pp+subs[i]); // add 'in-place' to soothe the later sorting algorithm
1521 }
1522 }
1523 } else {
1524 if( null != includes && includes.size() > 0) {
1525 boolean include = false;
1526 for(int i=0; !include && i<includes.size(); i++) {
1527 include = includes.get(i).matcher(p).matches();
1528 if( DEBUG ) {
1529 if( include ) {
1530 System.err.println("IOUtil.filesOf(): including <"+p+"> (including["+i+"]: "+includes.get(i)+")");
1531 }
1532 }
1533 }
1534 if( include ) {
1535 files.add(p);
1536 }
1537 } else {
1538 files.add(p);
1539 }
1540 }
1541 }
1542 return files;
1543 }
1544}
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:974
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 boolean testExeFile(final File file)
Test whether executable file exists, no OS executable tests performed, just permissions via Java.
Definition: IOUtil.java:953
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:1428
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:926
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:1275
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:1434
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:1047
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:1196
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:1481
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:1457
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...