diff options
Diffstat (limited to 'src/java/com/jogamp')
-rw-r--r-- | src/java/com/jogamp/common/nio/Buffers.java | 71 | ||||
-rw-r--r-- | src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java | 98 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/IOUtil.java | 167 |
3 files changed, 228 insertions, 108 deletions
diff --git a/src/java/com/jogamp/common/nio/Buffers.java b/src/java/com/jogamp/common/nio/Buffers.java index aae2be8..fb23627 100644 --- a/src/java/com/jogamp/common/nio/Buffers.java +++ b/src/java/com/jogamp/common/nio/Buffers.java @@ -39,6 +39,7 @@ */ package com.jogamp.common.nio; +import java.lang.reflect.Method; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -48,9 +49,14 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.ValueConv; +import jogamp.common.Debug; + /** * Utility methods allowing easy {@link java.nio.Buffer} manipulations. * @@ -60,6 +66,11 @@ import com.jogamp.common.util.ValueConv; */ public class Buffers { + static final boolean DEBUG; + static { + DEBUG = Debug.debug("Buffers"); + } + public static final int SIZEOF_BYTE = 1; public static final int SIZEOF_SHORT = 2; public static final int SIZEOF_CHAR = 2; @@ -1150,4 +1161,64 @@ public class Buffers { return sb; } + /** + * Access to NIO {@link sun.misc.Cleaner}, allowing caller to deterministically clean a given {@link sun.nio.ch.DirectBuffer}. + */ + public static class Cleaner { + private static final Method mbbCleaner; + private static final Method cClean; + private static final boolean hasCleaner; + /** OK to be lazy on thread synchronization, just for early out **/ + private static volatile boolean cleanerError; + static { + final Method[] _mbbCleaner = { null }; + final Method[] _cClean = { null }; + if( AccessController.doPrivileged(new PrivilegedAction<Boolean>() { + @Override + public Boolean run() { + try { + _mbbCleaner[0] = ReflectionUtil.getMethod("sun.nio.ch.DirectBuffer", "cleaner", null, Buffers.class.getClassLoader()); + _mbbCleaner[0].setAccessible(true); + _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean"); + _cClean[0].setAccessible(true); + return Boolean.TRUE; + } catch(final Throwable t) { + if( DEBUG ) { + System.err.println("Caught "+t.getMessage()); + t.printStackTrace(); + } + return Boolean.FALSE; + } } } ).booleanValue() ) { + mbbCleaner = _mbbCleaner[0]; + cClean = _cClean[0]; + hasCleaner = null != mbbCleaner && null != cClean; + } else { + mbbCleaner = null; + cClean = null; + hasCleaner = false; + } + cleanerError = !hasCleaner; + } + /** + * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer}, + * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method. + * @return {@code true} if successful, otherwise {@code false}. + */ + public static boolean clean(final Buffer b) { + if( !hasCleaner || cleanerError || !b.isDirect() ) { + return false; + } + try { + cClean.invoke(mbbCleaner.invoke(b)); + return true; + } catch(final Throwable t) { + cleanerError = true; + if( DEBUG ) { + System.err.println("Caught "+t.getMessage()); + t.printStackTrace(); + } + return false; + } + } + } } diff --git a/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java b/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java index f8d5857..6a56d6e 100644 --- a/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java +++ b/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java @@ -33,13 +33,10 @@ import java.io.OutputStream; import java.io.PrintStream; import java.io.RandomAccessFile; import java.lang.ref.WeakReference; -import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; -import java.security.AccessController; -import java.security.PrivilegedAction; import jogamp.common.Debug; @@ -163,10 +160,6 @@ public class MappedByteBufferInputStream extends InputStream { private int refCount; - private Method mbbCleaner; - private Method cClean; - private boolean cleanerInit; - private boolean hasCleaner; private CacheMode cmode; private int sliceIdx; @@ -191,10 +184,12 @@ public class MappedByteBufferInputStream extends InputStream { } } long fcSz = 0, pos = 0, rem = 0; - try { - fcSz = fc.size(); - } catch (final IOException e) { - e.printStackTrace(); + if( fc.isOpen() ) { + try { + fcSz = fc.size(); + } catch (final IOException e) { + e.printStackTrace(); + } } if( 0 < refCount ) { try { @@ -229,14 +224,16 @@ public class MappedByteBufferInputStream extends InputStream { notifyLengthChange( totalSize ); this.refCount = 1; - this.cleanerInit = false; - this.hasCleaner = false; this.cmode = cmode; this.sliceIdx = currSliceIdx; this.mark = -1; currentSlice().position(0); + + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("CTOR", System.err); + } } /** @@ -330,6 +327,9 @@ public class MappedByteBufferInputStream extends InputStream { } } } + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("Close", System.err); + } } final FileChannel.MapMode getMapMode() { return mmode; } @@ -441,10 +441,9 @@ public class MappedByteBufferInputStream extends InputStream { } position2( Math.min(prePosition, newTotalSize) ); // -> clipped position (set currSlice and re-map/-pos buffer) } - /* if( DEBUG ) { - System.err.println("notifyLengthChange.X: "+slices[currSlice]); - dbgDump("notifyLengthChange.X:", System.err); - } */ + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("NotifyLengthChange", System.err); + } } /** @@ -551,6 +550,21 @@ public class MappedByteBufferInputStream extends InputStream { } } + /** + * Releases the mapped {@link ByteBuffer} slices. + * @throws IOException if a buffer slice operation failed. + */ + public final synchronized void flushSlices() throws IOException { + if( null != slices ) { + for(int i=0; i<sliceCount; i++) { + flushSlice(i, synchronous); + } + } + if( MappedByteBufferInputStream.DEBUG ) { + this.dbgDump("FlushSlices", System.err); + } + } + synchronized void syncSlice(final ByteBuffer s) throws IOException { syncSlice(s, synchronous); } @@ -630,58 +644,16 @@ public class MappedByteBufferInputStream extends InputStream { } } private synchronized boolean cleanBuffer(final ByteBuffer mbb, final boolean syncBuffer) throws IOException { - if( !cleanerInit ) { - initCleaner(mbb); - } syncSlice(mbb, syncBuffer); if( !mbb.isDirect() ) { return false; } - boolean res = false; - if ( hasCleaner ) { - try { - cClean.invoke(mbbCleaner.invoke(mbb)); - res = true; - } catch(final Throwable t) { - hasCleaner = false; - if( DEBUG ) { - System.err.println("Caught "+t.getMessage()); - t.printStackTrace(); - } - } - } - if( !res && CacheMode.FLUSH_PRE_HARD == cmode ) { + if( !Buffers.Cleaner.clean(mbb) && CacheMode.FLUSH_PRE_HARD == cmode ) { cmode = CacheMode.FLUSH_PRE_SOFT; + return false; + } else { + return true; } - return res; - } - private synchronized void initCleaner(final ByteBuffer bb) { - final Method[] _mbbCleaner = { null }; - final Method[] _cClean = { null }; - AccessController.doPrivileged(new PrivilegedAction<Object>() { - @Override - public Object run() { - try { - _mbbCleaner[0] = bb.getClass().getMethod("cleaner"); - _mbbCleaner[0].setAccessible(true); - _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean"); - _cClean[0].setAccessible(true); - } catch(final Throwable t) { - if( DEBUG ) { - System.err.println("Caught "+t.getMessage()); - t.printStackTrace(); - } - } - return null; - } } ); - mbbCleaner = _mbbCleaner[0]; - cClean = _cClean[0]; - final boolean res = null != mbbCleaner && null != cClean; - if( DEBUG ) { - System.err.println("initCleaner: Has cleaner: "+res+", mbbCleaner "+mbbCleaner+", cClean "+cClean); - } - hasCleaner = res; - cleanerInit = true; } /** diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index 517c21c..0381ebc 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -43,10 +43,13 @@ import java.io.Reader; import java.io.SyncFailedException; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.regex.Pattern; import jogamp.common.Debug; @@ -54,6 +57,7 @@ import jogamp.common.os.AndroidUtils; import jogamp.common.os.PlatformPropsImpl; import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.JogampRuntimeException; import com.jogamp.common.net.AssetURLContext; import com.jogamp.common.net.Uri; import com.jogamp.common.nio.Buffers; @@ -65,14 +69,56 @@ public class IOUtil { private static final boolean DEBUG_EXE; private static final boolean DEBUG_EXE_NOSTREAM; private static final boolean DEBUG_EXE_EXISTING_FILE; + private static final boolean testTempDirExec; + private static final Method fileToPathGetter; + private static final Method isExecutableQuery; + private static final boolean useNativeExeFile; 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); + final boolean _props[] = { false, false, false, false, false, false }; + final Method[] res = AccessController.doPrivileged(new PrivilegedAction<Method[]>() { + @Override + public Method[] run() { + final Method[] res = new Method[] { null, null }; + try { + int i=0; + _props[i++] = Debug.debug("IOUtil"); + _props[i++] = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe", true); + _props[i++] = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.NoStream", true); + // For security reasons, we have to hardcode this, i.e. disable this manual debug feature! + _props[i++] = false; // PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.ExistingFile", true); + _props[i++] = PropertyAccess.getBooleanProperty("jogamp.gluegen.TestTempDirExec", true, true); + _props[i++] = PropertyAccess.getBooleanProperty("jogamp.gluegen.UseNativeExeFile", true, false); + + // Java 1.7 + i=0; + res[i] = File.class.getDeclaredMethod("toPath"); + res[i++].setAccessible(true); + final Class<?> nioPathClz = ReflectionUtil.getClass("java.nio.file.Path", false, IOUtil.class.getClassLoader()); + final Class<?> nioFilesClz = ReflectionUtil.getClass("java.nio.file.Files", false, IOUtil.class.getClassLoader()); + res[i] = nioFilesClz.getDeclaredMethod("isExecutable", nioPathClz); + res[i++].setAccessible(true); + } catch (final Throwable t) { + if(_props[0]) { + ExceptionUtils.dumpThrowable("ioutil-init", t); + } + } + return res; + } + }); + { + int i=0; + DEBUG = _props[i++]; + DEBUG_EXE = _props[i++]; + DEBUG_EXE_NOSTREAM = _props[i++]; + DEBUG_EXE_EXISTING_FILE = _props[i++]; + testTempDirExec = _props[i++]; + useNativeExeFile = _props[i++]; + + i=0; + fileToPathGetter = res[i++]; + isExecutableQuery = res[i++]; + } } /** Std. temporary directory property key <code>java.io.tmpdir</code>. */ @@ -708,7 +754,7 @@ public class IOUtil { private static String getExeTestFileSuffix() { switch(PlatformPropsImpl.OS_TYPE) { case WINDOWS: - if( Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { + if( useNativeExeFile && Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { return ".exe"; } else { return ".bat"; @@ -722,7 +768,7 @@ public class IOUtil { case WINDOWS: return "echo off"+PlatformPropsImpl.NEWLINE; default: - return null; + return "#!/bin/true"+PlatformPropsImpl.NEWLINE; } } private static String[] getExeTestCommandArgs(final String scriptFile) { @@ -749,7 +795,8 @@ public class IOUtil { private static WeakReference<byte[]> exeTestCodeRef = null; private static void fillExeTestFile(final File exefile) throws IOException { - if( Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE && + if( useNativeExeFile && + Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE && Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family ) { final byte[] exeTestCode; @@ -906,6 +953,18 @@ public class IOUtil { } } + private static final Boolean isNioExecutableFile(final File file) { + if( null != fileToPathGetter && null != isExecutableQuery ) { + try { + return (Boolean) isExecutableQuery.invoke(null, fileToPathGetter.invoke(file)); + } catch (final Throwable t) { + throw new JogampRuntimeException("error invoking Files.isExecutable(file.toPath())", t); + } + } else { + return null; + } + } + /** * Returns true if the given {@code dir} * <ol> @@ -923,6 +982,12 @@ public class IOUtil { { final boolean debug = DEBUG_EXE || DEBUG; + if( !testTempDirExec ) { + if(DEBUG) { + System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Disabled TestTempDirExec"); + } + return false; + } if (!testFile(dir, true, true)) { if( debug ) { System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Not writeable dir"); @@ -947,6 +1012,7 @@ public class IOUtil { } else { exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir); existingExe = false; + fillExeTestFile(exeTestFile); } } catch (final SecurityException se) { throw se; // fwd Security exception @@ -960,41 +1026,52 @@ public class IOUtil { long t2; int res = -1; int exitValue = -1; + Boolean isNioExec = null; if( existingExe || exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */) ) { - Process pr = null; - try { - 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! - // 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(); - 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 ) { - 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); + t2 = debug ? System.currentTimeMillis() : 0; + // First soft exec test via NIO's ACL check, if available + isNioExec = isNioExecutableFile(exeTestFile); + if( null != isNioExec ) { + res = isNioExec.booleanValue() ? 0 : -1; + } + if( null == isNioExec || 0 <= res ) { + // Hard exec test via actual execution, if NIO's ACL check succeeded or not available. + // Required, since Windows 'Software Restriction Policies (SRP)' won't be triggered merely by NIO's ACL check. + Process pr = null; + try { + // Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)' + // and hence splitting up command by spaces! + // 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(); + exitValue = pr.exitValue(); // Note: Bug 1219 Comment 50: On reporter's machine exit value 1 is being returned + if( 0 == exitValue ) { + res++; // file has been executed and exited normally + } else { + res = -2; // abnormal termination + } + } catch (final SecurityException se) { + throw se; // fwd Security exception + } catch (final Throwable t) { + t2 = debug ? System.currentTimeMillis() : 0; + res = -3; + 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); + } } } } @@ -1002,13 +1079,13 @@ public class IOUtil { t2 = debug ? System.currentTimeMillis() : 0; } - final boolean ok = 0 == res; + final boolean ok = 0 <= res; 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(): test-exe <"+exeTestFile.getAbsolutePath()+">, existingFile "+existingExe+", isNioExec "+isNioExec+", 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"); } |