aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/jogamp')
-rw-r--r--src/java/com/jogamp/common/nio/Buffers.java71
-rw-r--r--src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java98
-rw-r--r--src/java/com/jogamp/common/util/IOUtil.java167
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");
}