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