summaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/common/util/IOUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/jogamp/common/util/IOUtil.java')
-rw-r--r--src/java/com/jogamp/common/util/IOUtil.java299
1 files changed, 193 insertions, 106 deletions
diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java
index b9a0a33..342c557 100644
--- a/src/java/com/jogamp/common/util/IOUtil.java
+++ b/src/java/com/jogamp/common/util/IOUtil.java
@@ -40,6 +40,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
+import java.io.SyncFailedException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.net.URISyntaxException;
@@ -52,6 +53,7 @@ import jogamp.common.Debug;
import jogamp.common.os.AndroidUtils;
import jogamp.common.os.PlatformPropsImpl;
+import com.jogamp.common.ExceptionUtils;
import com.jogamp.common.net.AssetURLContext;
import com.jogamp.common.net.Uri;
import com.jogamp.common.nio.Buffers;
@@ -59,7 +61,19 @@ import com.jogamp.common.os.MachineDataInfo;
import com.jogamp.common.os.Platform;
public class IOUtil {
- public static final boolean DEBUG = Debug.debug("IOUtil");
+ public static final boolean DEBUG;
+ private static final boolean DEBUG_EXE;
+ private static final boolean DEBUG_EXE_NOSTREAM;
+ private static final boolean DEBUG_EXE_EXISTING_FILE;
+
+ static {
+ Debug.initSingleton();
+ DEBUG = Debug.debug("IOUtil");
+ DEBUG_EXE = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe", true);
+ DEBUG_EXE_NOSTREAM = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.NoStream", true);
+ // For security reasons, we have to hardcode this, i.e. disable this manual debug feature!
+ DEBUG_EXE_EXISTING_FILE = false; // PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.ExistingFile", true);
+ }
/** Std. temporary directory property key <code>java.io.tmpdir</code>. */
private static final String java_io_tmpdir_propkey = "java.io.tmpdir";
@@ -424,7 +438,7 @@ public class IOUtil {
/***
*
- * RESOURCE LOCATION STUFF
+ * RESOURCE LOCATION HELPER
*
*/
@@ -433,7 +447,10 @@ public class IOUtil {
* to be {@link #resolve(int) resolved} at a later time.
*/
public static class ClassResources {
- /** Class instance used to {@link #resolve(int)} the {@link #resourcePaths}. */
+ /** Optional {@link ClassLoader} used to {@link #resolve(int)} {@link #resourcePaths}. */
+ public final ClassLoader classLoader;
+
+ /** Optional class instance used to {@link #resolve(int)} relative {@link #resourcePaths}. */
public final Class<?> contextCL;
/** Resource paths, see {@link #resolve(int)}. */
@@ -443,67 +460,73 @@ public class IOUtil {
public final int resourceCount() { return resourcePaths.length; }
/**
- * @param contextCL class instance to {@link #resolve(int)} {@link #resourcePaths}.
- * @param resourcePaths array of strings denominating multiple resource paths. None shall be null.
+ * @param resourcePaths multiple relative or absolute resource locations
+ * @param classLoader optional {@link ClassLoader}, see {@link IOUtil#getResource(String, ClassLoader, Class)}
+ * @param relContext optional relative context, see {@link IOUtil#getResource(String, ClassLoader, Class)}
*/
- public ClassResources(final Class<?> contextCL, final String[] resourcePaths) {
+ public ClassResources(final String[] resourcePaths, final ClassLoader classLoader, final Class<?> relContext) {
for(int i=resourcePaths.length-1; i>=0; i--) {
if( null == resourcePaths[i] ) {
throw new IllegalArgumentException("resourcePath["+i+"] is null");
}
}
- this.contextCL = contextCL;
+ this.classLoader = classLoader;
+ this.contextCL = relContext;
this.resourcePaths = resourcePaths;
}
/**
- * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using {@link #contextCL} and {@link IOUtil#getResource(Class, String)}.
+ * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using
+ * {@link #classLoader}, {@link #contextCL} through {@link IOUtil#getResource(String, ClassLoader, Class)}.
* @throws ArrayIndexOutOfBoundsException if <code>uriIndex</code> is < 0 or >= {@link #resourceCount()}.
*/
public URLConnection resolve(final int uriIndex) throws ArrayIndexOutOfBoundsException {
- return getResource(contextCL, resourcePaths[uriIndex]);
+ return getResource(resourcePaths[uriIndex], classLoader, contextCL);
}
}
/**
* Locating a resource using {@link #getResource(String, ClassLoader)}:
* <ul>
- * <li><i>relative</i>: <code>context</code>'s package name-path plus <code>resourcePath</code> via <code>context</code>'s ClassLoader.
+ * <li><i>relative</i>: <code>relContext</code>'s package name-path plus <code>resourcePath</code> via <code>classLoader</code>.
* This allows locations relative to JAR- and other URLs.
- * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder.</li>
- * <li><i>absolute</i>: <code>context</code>'s ClassLoader and the <code>resourcePath</code> as is (filesystem)</li>
+ * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder.
+ * This attempt is skipped if {@code relContext} is {@code null}.</li>
+ * <li><i>absolute</i>: <code>resourcePath</code> as is via <code>classLoader</code>.
* </ul>
- *
* <p>
* Returns the resolved and open URLConnection or null if not found.
* </p>
*
+ * @param resourcePath the resource path to locate relative or absolute
+ * @param classLoader the optional {@link ClassLoader}, recommended
+ * @param relContext relative context, i.e. position, of the {@code resourcePath},
+ * to perform the relative lookup, if not {@code null}.
* @see #getResource(String, ClassLoader)
* @see ClassLoader#getResource(String)
* @see ClassLoader#getSystemResource(String)
*/
- public static URLConnection getResource(final Class<?> context, final String resourcePath) {
+ public static URLConnection getResource(final String resourcePath, final ClassLoader classLoader, final Class<?> relContext) {
if(null == resourcePath) {
return null;
}
- final ClassLoader contextCL = (null!=context)?context.getClassLoader():IOUtil.class.getClassLoader();
URLConnection conn = null;
- if(null != context) {
+ if(null != relContext) {
// scoping the path within the class's package
- final String className = context.getName().replace('.', '/');
+ final String className = relContext.getName().replace('.', '/');
final int lastSlash = className.lastIndexOf('/');
if (lastSlash >= 0) {
final String pkgName = className.substring(0, lastSlash + 1);
- conn = getResource(pkgName + resourcePath, contextCL);
+ conn = getResource(pkgName + resourcePath, classLoader);
if(DEBUG) {
- System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+context.getName()+">: "+(null!=conn));
+ System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+relContext.getName()+">: "+(null!=conn));
}
}
} else if(DEBUG) {
- System.err.println("IOUtil: null context");
+ System.err.println("IOUtil: null context, skip rel. lookup");
}
if(null == conn) {
- conn = getResource(resourcePath, contextCL);
+ conn = getResource(resourcePath, classLoader);
if(DEBUG) {
System.err.println("IOUtil: found <"+resourcePath+"> by classloader: "+(null!=conn));
}
@@ -534,8 +557,7 @@ public class IOUtil {
return AssetURLContext.createURL(resourcePath, cl).openConnection();
} catch (final IOException ioe) {
if(DEBUG) {
- System.err.println("IOUtil: Caught Exception:");
- ioe.printStackTrace();
+ ExceptionUtils.dumpThrowable("IOUtil", ioe);
}
return null;
}
@@ -544,8 +566,7 @@ public class IOUtil {
return AssetURLContext.resolve(resourcePath, cl);
} catch (final IOException ioe) {
if(DEBUG) {
- System.err.println("IOUtil: Caught Exception:");
- ioe.printStackTrace();
+ ExceptionUtils.dumpThrowable("IOUtil", ioe);
}
}
}
@@ -573,7 +594,7 @@ public class IOUtil {
}
/**
- * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory.
+ * @param path assuming a slashified path, either denotes a file or directory, either relative or absolute.
* @return parent of path
* @throws URISyntaxException if path is empty or has no parent directory available
*/
@@ -585,11 +606,11 @@ public class IOUtil {
final int e = path.lastIndexOf("/");
if( e < 0 ) {
- throw new URISyntaxException(path, "path contains no '/'");
+ throw new URISyntaxException(path, "path contains no '/': <"+path+">");
}
if( e == 0 ) {
// path is root directory
- throw new URISyntaxException(path, "path has no parents");
+ throw new URISyntaxException(path, "path has no parents: <"+path+">");
}
if( e < pl - 1 ) {
// path is file, return it's parent directory
@@ -599,23 +620,45 @@ public class IOUtil {
// path is a directory ..
final int p = path.lastIndexOf("/", e-1);
if( p >= j) {
+ // parent itself has '/' - post '!' or no '!' at all
return path.substring(0, p+1);
+ } else {
+ // parent itself has no '/'
+ final String parent = path.substring(j, e);
+ if( parent.equals("..") ) {
+ throw new URISyntaxException(path, "parent is unresolved: <"+path+">");
+ } else {
+ // parent is '!' or empty (relative path)
+ return path.substring(0, j);
+ }
}
- throw new URISyntaxException(path, "parent of path contains no '/'");
}
/**
- * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory.
- * @return clean path string where <code>../</code> and <code>./</code> is resolved.
+ * @param path assuming a slashified path, either denoting a file or directory, either relative or absolute.
+ * @return clean path string where {@code ./} and {@code ../} is resolved,
+ * while keeping a starting {@code ../} at the beginning of a relative path.
* @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
*/
public static String cleanPathString(String path) throws URISyntaxException {
- int idx;
- while ( ( idx = path.indexOf("../") ) >= 0 ) {
- path = getParentOf(path.substring(0, idx)) + path.substring(idx+3);
+ // Resolve './' before '../' to handle case 'parent/./../a.txt' properly.
+ int idx = path.length() - 1;
+ while ( idx >= 1 && ( idx = path.lastIndexOf("./", idx) ) >= 0 ) {
+ if( 0 < idx && path.charAt(idx-1) == '.' ) {
+ idx-=2; // skip '../' -> idx upfront
+ } else {
+ path = path.substring(0, idx) + path.substring(idx+2);
+ idx--; // idx upfront
+ }
}
- while ( ( idx = path.indexOf("./") ) >= 0 ) {
- path = path.substring(0, idx) + path.substring(idx+2);
+ idx = 0;
+ while ( ( idx = path.indexOf("../", idx) ) >= 0 ) {
+ if( 0 == idx ) {
+ idx += 3; // skip starting '../'
+ } else {
+ path = getParentOf(path.substring(0, idx)) + path.substring(idx+3);
+ idx = 0;
+ }
}
return path;
}
@@ -658,8 +701,7 @@ public class IOUtil {
return c;
} catch (final IOException ioe) {
if(DEBUG) {
- System.err.println("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false - "+ioe.getClass().getSimpleName()+": "+ioe.getMessage());
- ioe.printStackTrace();
+ ExceptionUtils.dumpThrowable("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false -", ioe);
}
}
} else if(DEBUG) {
@@ -697,48 +739,49 @@ public class IOUtil {
return new String[] { scriptFile };
}
}
- private static final byte[] getBytesFromRelFile(final byte[] res, final String fname, final int size) throws IOException {
- final URLConnection con = IOUtil.getResource(IOUtil.class, fname);
+
+ private static final byte[] readCode(final String fname) throws IOException {
+ final URLConnection con = IOUtil.getResource(fname, IOUtil.class.getClassLoader(), IOUtil.class);
final InputStream in = con.getInputStream();
- int numBytes = 0;
+ byte[] output = null;
try {
- while (true) {
- final int remBytes = size - numBytes;
- int count;
- if ( 0 >= remBytes || (count = in.read(res, numBytes, remBytes)) == -1 ) {
- break;
- }
- numBytes += count;
- }
+ output = CustomCompress.inflateFromStream(in);
} finally {
in.close();
}
- if( size != numBytes ) {
- throw new IOException("Got "+numBytes+" bytes != expected "+size);
- }
- return res;
+ return output;
}
- private static final Object exeTestBytesLock = new Object();
- private static WeakReference<byte[]> exeTestBytesRef = null;
+ private static final Object exeTestLock = new Object();
+ private static WeakReference<byte[]> exeTestCodeRef = null;
private static void fillExeTestFile(final File exefile) throws IOException {
if( Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE &&
Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family
) {
- final int codeSize = 268;
- final byte[] code;
- synchronized ( exeTestBytesLock ) {
- byte[] _code;
- if( null == exeTestBytesRef || null == ( _code = exeTestBytesRef.get() ) ) {
- code = getBytesFromRelFile(new byte[512], "bin/exe-windows-i586-268b.bin", codeSize);
- exeTestBytesRef = new WeakReference<byte[]>(code);
+ final byte[] exeTestCode;
+ synchronized ( exeTestLock ) {
+ byte[] _exeTestCode = null;
+ if( null == exeTestCodeRef || null == ( _exeTestCode = exeTestCodeRef.get() ) ) {
+ final String fname;
+ if( Platform.CPUType.X86_64 == PlatformPropsImpl.CPU_ARCH ) {
+ fname = "bin/exe-windows-x86_64.defl";
+ } else {
+ fname = "bin/exe-windows-i386.defl";
+ }
+ exeTestCode = readCode(fname);
+ exeTestCodeRef = new WeakReference<byte[]>(exeTestCode);
} else {
- code = _code;
+ exeTestCode = _exeTestCode;
}
}
- final OutputStream out = new FileOutputStream(exefile);
+ final FileOutputStream out = new FileOutputStream(exefile);
try {
- out.write(code, 0, codeSize);
+ out.write(exeTestCode, 0, exeTestCode.length);
+ try {
+ out.getFD().sync();
+ } catch (final SyncFailedException sfe) {
+ ExceptionUtils.dumpThrowable("", sfe);
+ }
} finally {
out.close();
}
@@ -748,6 +791,11 @@ public class IOUtil {
final FileWriter fout = new FileWriter(exefile);
try {
fout.write(shellCode);
+ try {
+ fout.flush();
+ } catch (final IOException sfe) {
+ ExceptionUtils.dumpThrowable("", sfe);
+ }
} finally {
fout.close();
}
@@ -812,44 +860,52 @@ public class IOUtil {
public static class StreamMonitor implements Runnable {
private final InputStream[] istreams;
+ private final boolean[] eos;
private final PrintStream ostream;
private final String prefix;
public StreamMonitor(final InputStream[] streams, final PrintStream ostream, final String prefix) {
this.istreams = streams;
+ this.eos = new boolean[streams.length];
this.ostream = ostream;
this.prefix = prefix;
- new Thread(this, "StreamMonitor-"+Thread.currentThread().getName()).start();
+ final InterruptSource.Thread t = new InterruptSource.Thread(null, this, "StreamMonitor-"+Thread.currentThread().getName());
+ t.setDaemon(true);
+ t.start();
}
+
@Override
public void run()
{
final byte[] buffer = new byte[4096];
try {
- int numRead;
+ final int streamCount = istreams.length;
+ int eosCount = 0;
do {
- numRead = 0;
for(int i=0; i<istreams.length; i++) {
- final int numReadI = istreams[i].read(buffer);
- if (numReadI > 0) {
- if( null != ostream ) {
- if( null != prefix ) {
- ostream.write(prefix.getBytes());
+ if( !eos[i] ) {
+ final int numReadI = istreams[i].read(buffer);
+ if (numReadI > 0) {
+ if( null != ostream ) {
+ if( null != prefix ) {
+ ostream.write(prefix.getBytes());
+ }
+ ostream.write(buffer, 0, numReadI);
}
- ostream.write(buffer, 0, numReadI);
+ } else {
+ // numReadI == -1
+ eosCount++;
+ eos[i] = true;
}
- numRead += numReadI;
}
}
if( null != ostream ) {
ostream.flush();
}
- } while (numRead >= 0);
- }
- catch (final IOException e) {
- for(int i=0; i<istreams.length; i++) {
- try {
- istreams[i].close();
- } catch (final IOException e2) { }
+ } while ( eosCount < streamCount );
+ } catch (final IOException e) {
+ } finally {
+ if( null != ostream ) {
+ ostream.flush();
}
// Should allow clean exit when process shuts down
}
@@ -871,6 +927,8 @@ public class IOUtil {
public static boolean testDirExec(final File dir)
throws SecurityException
{
+ final boolean debug = DEBUG_EXE || DEBUG;
+
if( !testTempDirExec ) {
if(DEBUG) {
System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Disabled "+_TestTempDirExec);
@@ -878,64 +936,93 @@ public class IOUtil {
return false;
}
if (!testFile(dir, true, true)) {
- if(DEBUG) {
+ if( debug ) {
System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Not writeable dir");
}
return false;
}
if(!getOSHasNoexecFS()) {
- if(DEBUG) {
+ if( debug ) {
System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Always executable");
}
return true;
}
- final long t0 = DEBUG ? System.currentTimeMillis() : 0;
+ final long t0 = debug ? System.currentTimeMillis() : 0;
final File exeTestFile;
+ final boolean existingExe;
try {
- exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir);
+ final File permExeTestFile = DEBUG_EXE_EXISTING_FILE ? new File(dir, "jogamp_exe_tst"+getExeTestFileSuffix()) : null;
+ if( null != permExeTestFile && permExeTestFile.exists() ) {
+ exeTestFile = permExeTestFile;
+ existingExe = true;
+ } else {
+ exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir);
+ existingExe = false;
+ }
} catch (final SecurityException se) {
throw se; // fwd Security exception
} catch (final IOException e) {
- if(DEBUG) {
+ if( debug ) {
e.printStackTrace();
}
return false;
}
- final long t1 = DEBUG ? System.currentTimeMillis() : 0;
+ final long t1 = debug ? System.currentTimeMillis() : 0;
+ long t2;
int res = -1;
- if(exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */)) {
+ int exitValue = -1;
+ if( existingExe || exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */) ) {
+ Process pr = null;
try {
- fillExeTestFile(exeTestFile);
-
+ if( !existingExe ) {
+ fillExeTestFile(exeTestFile);
+ }
+ t2 = debug ? System.currentTimeMillis() : 0;
// Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)'
// and hence splitting up command by spaces!
- final Process pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeTestFile.getCanonicalPath() ) );
- /**
- * Disable StreamMonitor, which throttles exec-test performance a lot!
- *
- * if( isStringSet(shellCode) ) {
+ // Note: All no-exec cases throw an IOExceptions at ProcessBuilder.start(), i.e. below exec() call!
+ pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeTestFile.getCanonicalPath() ), null, null );
+ if( DEBUG_EXE && !DEBUG_EXE_NOSTREAM ) {
new StreamMonitor(new InputStream[] { pr.getInputStream(), pr.getErrorStream() }, System.err, "Exe-Tst: ");
- }
- */
- pr.waitFor() ;
- res = pr.exitValue();
+ }
+ pr.waitFor();
+ exitValue = pr.exitValue(); // Note: Bug 1219 Comment 50: On reporter's machine exit value 1 is being returned
+ res = 0; // file has been executed
} catch (final SecurityException se) {
throw se; // fwd Security exception
} catch (final Throwable t) {
+ t2 = debug ? System.currentTimeMillis() : 0;
res = -2;
- if(DEBUG) {
+ if( debug ) {
System.err.println("IOUtil.testDirExec: <"+exeTestFile.getAbsolutePath()+">: Caught "+t.getClass().getSimpleName()+": "+t.getMessage());
t.printStackTrace();
}
+ } finally {
+ if( null != pr ) {
+ // Bug 1219 Comment 58: Ensure that the launched process gets terminated!
+ // This is Process implementation specific and varies on different platforms,
+ // hence it may be required.
+ try {
+ pr.destroy();
+ } catch (final Throwable t) {
+ ExceptionUtils.dumpThrowable("", t);
+ }
+ }
}
+ } else {
+ t2 = debug ? System.currentTimeMillis() : 0;
}
+
final boolean ok = 0 == res;
- final long t2 = DEBUG ? System.currentTimeMillis() : 0;
- exeTestFile.delete();
- if( DEBUG) {
- System.err.println("IOUtil.testDirExec(): <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok);
- System.err.println("IOUtil.testDirExec(): total "+(t2-t0)+"ms, create "+(t1-t0)+"ms, execute "+(t2-t1)+"ms");
+ if( !DEBUG_EXE && !existingExe ) {
+ exeTestFile.delete();
+ }
+ if( debug ) {
+ final long t3 = System.currentTimeMillis();
+ System.err.println("IOUtil.testDirExec(): test-exe <"+exeTestFile.getAbsolutePath()+">, existingFile "+existingExe+", returned "+exitValue);
+ System.err.println("IOUtil.testDirExec(): abs-path <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok);
+ System.err.println("IOUtil.testDirExec(): total "+(t3-t0)+"ms, create "+(t1-t0)+"ms, fill "+(t2-t1)+"ms, execute "+(t3-t2)+"ms");
}
return ok;
}