diff --git a/jme3-core/src/main/java/com/jme3/util/BufferAllocator.java b/jme3-core/src/main/java/com/jme3/util/BufferAllocator.java
new file mode 100644
index 000000000..e7d429db6
--- /dev/null
+++ b/jme3-core/src/main/java/com/jme3/util/BufferAllocator.java
@@ -0,0 +1,12 @@
+package com.jme3.util;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+
+public interface BufferAllocator {
+
+ void destroyDirectBuffer(Buffer toBeDestroyed);
+
+ ByteBuffer allocate(int size);
+
+}
diff --git a/jme3-core/src/main/java/com/jme3/util/BufferUtils.java b/jme3-core/src/main/java/com/jme3/util/BufferUtils.java
index 20189afb1..6ad4712b8 100644
--- a/jme3-core/src/main/java/com/jme3/util/BufferUtils.java
+++ b/jme3-core/src/main/java/com/jme3/util/BufferUtils.java
@@ -31,17 +31,10 @@
*/
package com.jme3.util;
-import com.jme3.math.ColorRGBA;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
-import com.jme3.math.Vector4f;
import java.io.UnsupportedEncodingException;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -51,8 +44,12 @@ import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
/**
* BufferUtils
is a helper class for generating nio buffers from
@@ -62,27 +59,52 @@ import java.util.logging.Logger;
* @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $
*/
public final class BufferUtils {
+ private static BufferAllocator allocator = new PrimitiveAllocator();
private static boolean trackDirectMemory = false;
private static ReferenceQueue removeCollected = new ReferenceQueue();
private static ConcurrentHashMap trackedBuffers = new ConcurrentHashMap();
static ClearReferences cleanupthread;
+ private static boolean used;
+
+ static {
+ try {
+ allocator = new ReflectionAllocator();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ System.err.println("Error using ReflectionAllocator");
+ }
+ }
+
+ /**
+ * Warning! do only set this before JME is started!
+ */
+ public static void setAllocator(BufferAllocator allocator) {
+ if (used) {
+ throw new IllegalStateException(
+ "An Buffer was already allocated, since it is quite likely that other dispose methods will create native dangling pointers or other fun things, this is forbidden to be changed at runtime");
+ }
+ BufferUtils.allocator = allocator;
+ }
+
/**
- * Set it to true if you want to enable direct memory tracking for debugging purpose.
- * Default is false.
- * To print direct memory usage use BufferUtils.printCurrentDirectMemory(StringBuilder store);
- * @param enabled
+ * Set it to true if you want to enable direct memory tracking for debugging
+ * purpose. Default is false. To print direct memory usage use
+ * BufferUtils.printCurrentDirectMemory(StringBuilder store);
+ *
+ * @param enabled
*/
public static void setTrackDirectMemoryEnabled(boolean enabled) {
trackDirectMemory = enabled;
}
/**
- * Creates a clone of the given buffer. The clone's capacity is
- * equal to the given buffer's limit.
+ * Creates a clone of the given buffer. The clone's capacity is equal to the
+ * given buffer's limit.
*
- * @param buf The buffer to clone
+ * @param buf
+ * The buffer to clone
* @return The cloned buffer
*/
public static Buffer clone(Buffer buf) {
@@ -102,55 +124,31 @@ public final class BufferUtils {
}
private static void onBufferAllocated(Buffer buffer) {
- /**
- * StackTraceElement[] stackTrace = new Throwable().getStackTrace(); int
- * initialIndex = 0;
- *
- * for (int i = 0; i < stackTrace.length; i++){ if
- * (!stackTrace[i].getClassName().equals(BufferUtils.class.getName())){
- * initialIndex = i; break; } }
- *
- * int allocated = buffer.capacity(); int size = 0;
- *
- * if (buffer instanceof FloatBuffer){ size = 4; }else if (buffer
- * instanceof ShortBuffer){ size = 2; }else if (buffer instanceof
- * ByteBuffer){ size = 1; }else if (buffer instanceof IntBuffer){ size =
- * 4; }else if (buffer instanceof DoubleBuffer){ size = 8; }
- *
- * allocated *= size;
- *
- * for (int i = initialIndex; i < stackTrace.length; i++){
- * StackTraceElement element = stackTrace[i]; if
- * (element.getClassName().startsWith("java")){ break; }
- *
- * try { Class clazz = Class.forName(element.getClassName()); if (i ==
- * initialIndex){
- * System.out.println(clazz.getSimpleName()+"."+element.getMethodName
- * ()+"():" + element.getLineNumber() + " allocated " + allocated);
- * }else{ System.out.println(" at " +
- * clazz.getSimpleName()+"."+element.getMethodName()+"()"); } } catch
- * (ClassNotFoundException ex) { } }
- */
+ used = true;
if (BufferUtils.trackDirectMemory) {
-
if (BufferUtils.cleanupthread == null) {
BufferUtils.cleanupthread = new ClearReferences();
BufferUtils.cleanupthread.start();
}
if (buffer instanceof ByteBuffer) {
- BufferInfo info = new BufferInfo(ByteBuffer.class, buffer.capacity(), buffer, BufferUtils.removeCollected);
+ BufferInfo info = new BufferInfo(ByteBuffer.class, buffer.capacity(), buffer,
+ BufferUtils.removeCollected);
BufferUtils.trackedBuffers.put(info, info);
} else if (buffer instanceof FloatBuffer) {
- BufferInfo info = new BufferInfo(FloatBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);
+ BufferInfo info = new BufferInfo(FloatBuffer.class, buffer.capacity() * 4, buffer,
+ BufferUtils.removeCollected);
BufferUtils.trackedBuffers.put(info, info);
} else if (buffer instanceof IntBuffer) {
- BufferInfo info = new BufferInfo(IntBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);
+ BufferInfo info = new BufferInfo(IntBuffer.class, buffer.capacity() * 4, buffer,
+ BufferUtils.removeCollected);
BufferUtils.trackedBuffers.put(info, info);
} else if (buffer instanceof ShortBuffer) {
- BufferInfo info = new BufferInfo(ShortBuffer.class, buffer.capacity() * 2, buffer, BufferUtils.removeCollected);
+ BufferInfo info = new BufferInfo(ShortBuffer.class, buffer.capacity() * 2, buffer,
+ BufferUtils.removeCollected);
BufferUtils.trackedBuffers.put(info, info);
} else if (buffer instanceof DoubleBuffer) {
- BufferInfo info = new BufferInfo(DoubleBuffer.class, buffer.capacity() * 8, buffer, BufferUtils.removeCollected);
+ BufferInfo info = new BufferInfo(DoubleBuffer.class, buffer.capacity() * 8, buffer,
+ BufferUtils.removeCollected);
BufferUtils.trackedBuffers.put(info, info);
}
@@ -158,11 +156,12 @@ public final class BufferUtils {
}
/**
- * Generate a new FloatBuffer using the given array of Vector3f objects.
- * The FloatBuffer will be 3 * data.length long and contain the vector data
- * as data[0].x, data[0].y, data[0].z, data[1].x... etc.
+ * Generate a new FloatBuffer using the given array of Vector3f objects. The
+ * FloatBuffer will be 3 * data.length long and contain the vector data as
+ * data[0].x, data[0].y, data[0].z, data[1].x... etc.
*
- * @param data array of Vector3f objects to place into a new FloatBuffer
+ * @param data
+ * array of Vector3f objects to place into a new FloatBuffer
*/
public static FloatBuffer createFloatBuffer(Vector3f... data) {
if (data == null) {
@@ -184,7 +183,8 @@ public final class BufferUtils {
* Generate a new FloatBuffer using the given array of Quaternion objects.
* The FloatBuffer will be 4 * data.length long and contain the vector data.
*
- * @param data array of Quaternion objects to place into a new FloatBuffer
+ * @param data
+ * array of Quaternion objects to place into a new FloatBuffer
*/
public static FloatBuffer createFloatBuffer(Quaternion... data) {
if (data == null) {
@@ -203,10 +203,11 @@ public final class BufferUtils {
}
/**
- * Generate a new FloatBuffer using the given array of Vector4 objects.
- * The FloatBuffer will be 4 * data.length long and contain the vector data.
+ * Generate a new FloatBuffer using the given array of Vector4 objects. The
+ * FloatBuffer will be 4 * data.length long and contain the vector data.
*
- * @param data array of Vector4 objects to place into a new FloatBuffer
+ * @param data
+ * array of Vector4 objects to place into a new FloatBuffer
*/
public static FloatBuffer createFloatBuffer(Vector4f... data) {
if (data == null) {
@@ -228,7 +229,8 @@ public final class BufferUtils {
* Generate a new FloatBuffer using the given array of ColorRGBA objects.
* The FloatBuffer will be 4 * data.length long and contain the color data.
*
- * @param data array of ColorRGBA objects to place into a new FloatBuffer
+ * @param data
+ * array of ColorRGBA objects to place into a new FloatBuffer
*/
public static FloatBuffer createFloatBuffer(ColorRGBA... data) {
if (data == null) {
@@ -248,7 +250,9 @@ public final class BufferUtils {
/**
* Generate a new FloatBuffer using the given array of float primitives.
- * @param data array of float primitives to place into a new FloatBuffer
+ *
+ * @param data
+ * array of float primitives to place into a new FloatBuffer
*/
public static FloatBuffer createFloatBuffer(float... data) {
if (data == null) {
@@ -307,8 +311,7 @@ public final class BufferUtils {
* @param index
* the postion to place the data; in terms of colors not floats
*/
- public static void setInBuffer(ColorRGBA color, FloatBuffer buf,
- int index) {
+ public static void setInBuffer(ColorRGBA color, FloatBuffer buf, int index) {
buf.position(index * 4);
buf.put(color.r);
buf.put(color.g);
@@ -317,18 +320,18 @@ public final class BufferUtils {
}
/**
- * Sets the data contained in the given quaternion into the FloatBuffer at the
- * specified index.
+ * Sets the data contained in the given quaternion into the FloatBuffer at
+ * the specified index.
*
* @param quat
* the {@link Quaternion} to insert
* @param buf
* the buffer to insert into
* @param index
- * the position to place the data; in terms of quaternions not floats
+ * the position to place the data; in terms of quaternions not
+ * floats
*/
- public static void setInBuffer(Quaternion quat, FloatBuffer buf,
- int index) {
+ public static void setInBuffer(Quaternion quat, FloatBuffer buf, int index) {
buf.position(index * 4);
buf.put(quat.getX());
buf.put(quat.getY());
@@ -347,8 +350,7 @@ public final class BufferUtils {
* @param index
* the position to place the data; in terms of vector4 not floats
*/
- public static void setInBuffer(Vector4f vec, FloatBuffer buf,
- int index) {
+ public static void setInBuffer(Vector4f vec, FloatBuffer buf, int index) {
buf.position(index * 4);
buf.put(vec.getX());
buf.put(vec.getY());
@@ -438,8 +440,8 @@ public final class BufferUtils {
/**
* Copies a Vector3f from one position in the buffer to another. The index
- * values are in terms of vector number (eg, vector number 0 is positions 0-2
- * in the FloatBuffer.)
+ * values are in terms of vector number (eg, vector number 0 is positions
+ * 0-2 in the FloatBuffer.)
*
* @param buf
* the buffer to copy from/to
@@ -534,11 +536,12 @@ public final class BufferUtils {
// // -- VECTOR2F METHODS -- ////
/**
- * Generate a new FloatBuffer using the given array of Vector2f objects.
- * The FloatBuffer will be 2 * data.length long and contain the vector data
- * as data[0].x, data[0].y, data[1].x... etc.
+ * Generate a new FloatBuffer using the given array of Vector2f objects. The
+ * FloatBuffer will be 2 * data.length long and contain the vector data as
+ * data[0].x, data[0].y, data[1].x... etc.
*
- * @param data array of Vector2f objects to place into a new FloatBuffer
+ * @param data
+ * array of Vector2f objects to place into a new FloatBuffer
*/
public static FloatBuffer createFloatBuffer(Vector2f... data) {
if (data == null) {
@@ -643,8 +646,8 @@ public final class BufferUtils {
/**
* Copies a Vector2f from one position in the buffer to another. The index
- * values are in terms of vector number (eg, vector number 0 is positions 0-1
- * in the FloatBuffer.)
+ * values are in terms of vector number (eg, vector number 0 is positions
+ * 0-1 in the FloatBuffer.)
*
* @param buf
* the buffer to copy from/to
@@ -737,7 +740,7 @@ public final class BufferUtils {
return eq;
}
- //// -- INT METHODS -- ////
+ //// -- INT METHODS -- ////
/**
* Generate a new IntBuffer using the given array of ints. The IntBuffer
* will be data.length long and contain the int data as data[0], data[1]...
@@ -806,7 +809,7 @@ public final class BufferUtils {
* @return the new DoubleBuffer
*/
public static DoubleBuffer createDoubleBuffer(int size) {
- DoubleBuffer buf = ByteBuffer.allocateDirect(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();
+ DoubleBuffer buf = allocator.allocate(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();
buf.clear();
onBufferAllocated(buf);
return buf;
@@ -869,7 +872,7 @@ public final class BufferUtils {
* @return the new FloatBuffer
*/
public static FloatBuffer createFloatBuffer(int size) {
- FloatBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ FloatBuffer buf = allocator.allocate(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();
buf.clear();
onBufferAllocated(buf);
return buf;
@@ -931,7 +934,7 @@ public final class BufferUtils {
* @return the new IntBuffer
*/
public static IntBuffer createIntBuffer(int size) {
- IntBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();
+ IntBuffer buf = allocator.allocate(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();
buf.clear();
onBufferAllocated(buf);
return buf;
@@ -994,7 +997,7 @@ public final class BufferUtils {
* @return the new IntBuffer
*/
public static ByteBuffer createByteBuffer(int size) {
- ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
+ ByteBuffer buf = allocator.allocate(size).order(ByteOrder.nativeOrder());
buf.clear();
onBufferAllocated(buf);
return buf;
@@ -1076,7 +1079,7 @@ public final class BufferUtils {
* @return the new ShortBuffer
*/
public static ShortBuffer createShortBuffer(int size) {
- ShortBuffer buf = ByteBuffer.allocateDirect(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();
+ ShortBuffer buf = allocator.allocate(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();
buf.clear();
onBufferAllocated(buf);
return buf;
@@ -1115,9 +1118,9 @@ public final class BufferUtils {
}
/**
- * Creates a new ShortBuffer with the same contents as the given ShortBuffer.
- * The new ShortBuffer is separate from the old one and changes are not
- * reflected across. If you want to reflect changes, consider using
+ * Creates a new ShortBuffer with the same contents as the given
+ * ShortBuffer. The new ShortBuffer is separate from the old one and changes
+ * are not reflected across. If you want to reflect changes, consider using
* Buffer.duplicate().
*
* @param buf
@@ -1142,12 +1145,18 @@ public final class BufferUtils {
}
/**
- * Ensures there is at least the required
number of entries left after the current position of the
- * buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer.
- * @param buffer buffer that should be checked/copied (may be null)
- * @param required minimum number of elements that should be remaining in the returned buffer
- * @return a buffer large enough to receive at least the required
number of entries, same position as
- * the input buffer, not null
+ * Ensures there is at least the required
number of entries
+ * left after the current position of the buffer. If the buffer is too small
+ * a larger one is created and the old one copied to the new buffer.
+ *
+ * @param buffer
+ * buffer that should be checked/copied (may be null)
+ * @param required
+ * minimum number of elements that should be remaining in the
+ * returned buffer
+ * @return a buffer large enough to receive at least the
+ * required
number of entries, same position as the
+ * input buffer, not null
*/
public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) {
if (buffer != null) {
@@ -1165,7 +1174,7 @@ public final class BufferUtils {
}
return buffer;
}
-
+
public static IntBuffer ensureLargeEnough(IntBuffer buffer, int required) {
if (buffer != null) {
buffer.limit(buffer.capacity());
@@ -1182,7 +1191,6 @@ public final class BufferUtils {
}
return buffer;
}
-
public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {
if (buffer != null) {
@@ -1255,108 +1263,46 @@ public final class BufferUtils {
}
store.append("Existing buffers: ").append(BufferUtils.trackedBuffers.size()).append("\n");
- store.append("(b: ").append(bBufs).append(" f: ").append(fBufs).append(" i: ").append(iBufs).append(" s: ").append(sBufs).append(" d: ").append(dBufs).append(")").append("\n");
+ store.append("(b: ").append(bBufs).append(" f: ").append(fBufs).append(" i: ").append(iBufs)
+ .append(" s: ").append(sBufs).append(" d: ").append(dBufs).append(")").append("\n");
store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");
store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n");
- store.append("(b: ").append(bBufsM / 1024).append("kb f: ").append(fBufsM / 1024).append("kb i: ").append(iBufsM / 1024).append("kb s: ").append(sBufsM / 1024).append("kb d: ").append(dBufsM / 1024).append("kb)").append("\n");
+ store.append("(b: ").append(bBufsM / 1024).append("kb f: ").append(fBufsM / 1024).append("kb i: ")
+ .append(iBufsM / 1024).append("kb s: ").append(sBufsM / 1024).append("kb d: ")
+ .append(dBufsM / 1024).append("kb)").append("\n");
} else {
store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");
- store.append("Only heap memory available, if you want to monitor direct memory use BufferUtils.setTrackDirectMemoryEnabled(true) during initialization.").append("\n");
+ store.append(
+ "Only heap memory available, if you want to monitor direct memory use BufferUtils.setTrackDirectMemoryEnabled(true) during initialization.")
+ .append("\n");
}
if (printStout) {
System.out.println(store.toString());
}
}
- private static Method cleanerMethod = null;
- private static Method cleanMethod = null;
- private static Method viewedBufferMethod = null;
- private static Method freeMethod = null;
-
- private static Method loadMethod(String className, String methodName) {
- try {
- Method method = Class.forName(className).getMethod(methodName);
- method.setAccessible(true);
- return method;
- } catch (NoSuchMethodException ex) {
- return null; // the method was not found
- } catch (SecurityException ex) {
- return null; // setAccessible not allowed by security policy
- } catch (ClassNotFoundException ex) {
- return null; // the direct buffer implementation was not found
- }
- }
-
- static {
- // Oracle JRE / OpenJDK
- cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");
- cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
- viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");
- if (viewedBufferMethod == null) {
- // They changed the name in Java 7 (???)
- viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "attachment");
- }
-
- // Apache Harmony
- ByteBuffer bb = BufferUtils.createByteBuffer(1);
- Class> clazz = bb.getClass();
- try {
- freeMethod = clazz.getMethod("free");
- } catch (NoSuchMethodException ex) {
- } catch (SecurityException ex) {
- }
- }
/**
* Direct buffers are garbage collected by using a phantom reference and a
- * reference queue. Every once a while, the JVM checks the reference queue and
- * cleans the direct buffers. However, as this doesn't happen
- * immediately after discarding all references to a direct buffer, it's
- * easy to OutOfMemoryError yourself using direct buffers. This function
- * explicitly calls the Cleaner method of a direct buffer.
- *
- * @param toBeDestroyed
- * The direct buffer that will be "cleaned". Utilizes reflection.
- *
- */
+ * reference queue. Every once a while, the JVM checks the reference queue
+ * and cleans the direct buffers. However, as this doesn't happen
+ * immediately after discarding all references to a direct buffer, it's easy
+ * to OutOfMemoryError yourself using direct buffers.
+ **/
public static void destroyDirectBuffer(Buffer toBeDestroyed) {
if (!isDirect(toBeDestroyed)) {
return;
}
-
- try {
- if (freeMethod != null) {
- freeMethod.invoke(toBeDestroyed);
- } else {
- Object cleaner = cleanerMethod.invoke(toBeDestroyed);
- if (cleaner != null) {
- cleanMethod.invoke(cleaner);
- } else {
- // Try the alternate approach of getting the viewed buffer first
- Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);
- if (viewedBuffer != null) {
- destroyDirectBuffer((Buffer) viewedBuffer);
- } else {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed);
- }
- }
- }
- } catch (IllegalAccessException ex) {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
- } catch (IllegalArgumentException ex) {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
- } catch (InvocationTargetException ex) {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
- } catch (SecurityException ex) {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
- }
+ allocator.destroyDirectBuffer(toBeDestroyed);
}
-
+
/*
- * FIXME when java 1.5 supprt is dropped - replace calls to this method with Buffer.isDirect
+ * FIXME when java 1.5 supprt is dropped - replace calls to this method with
+ * Buffer.isDirect
*
- * Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer subclasses :
- * FloatBuffer, IntBuffer, ShortBuffer, ByteBuffer,DoubleBuffer, LongBuffer.
- * CharBuffer has been excluded as we don't use it.
+ * Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer
+ * subclasses : FloatBuffer, IntBuffer, ShortBuffer,
+ * ByteBuffer,DoubleBuffer, LongBuffer. CharBuffer has been excluded as we
+ * don't use it.
*
*/
private static boolean isDirect(Buffer buf) {
diff --git a/jme3-core/src/main/java/com/jme3/util/PrimitiveAllocator.java b/jme3-core/src/main/java/com/jme3/util/PrimitiveAllocator.java
new file mode 100644
index 000000000..d49c02105
--- /dev/null
+++ b/jme3-core/src/main/java/com/jme3/util/PrimitiveAllocator.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+
+/**
+ * This class contains a primitve allocator with no special logic, should work
+ * on any jvm
+ */
+public final class PrimitiveAllocator implements BufferAllocator {
+
+ @Override
+ public void destroyDirectBuffer(Buffer toBeDestroyed) {
+ // no exception by intent, as this way naivly written java7/8
+ // applications wont crash on 9 assuming they can dispose buffers
+ System.err.println("Warning destroyBuffer not supported");
+ }
+
+ @Override
+ public ByteBuffer allocate(int size) {
+ return ByteBuffer.allocateDirect(size);
+ }
+
+}
diff --git a/jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java b/jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java
new file mode 100644
index 000000000..de7e75bcb
--- /dev/null
+++ b/jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class contains the reflection based way to remove DirectByteBuffers in
+ * java < 9, allocation is done via ByteBuffer.allocateDirect
+ */
+public final class ReflectionAllocator implements BufferAllocator {
+ private static Method cleanerMethod = null;
+ private static Method cleanMethod = null;
+ private static Method viewedBufferMethod = null;
+ private static Method freeMethod = null;
+
+ static {
+ // Oracle JRE / OpenJDK
+ cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");
+ cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
+ viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");
+ if (viewedBufferMethod == null) {
+ // They changed the name in Java 7 (???)
+ viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "attachment");
+ }
+
+ // Apache Harmony (allocated directly, to not trigger allocator used
+ // logic in BufferUtils)
+ ByteBuffer bb = ByteBuffer.allocateDirect(1);
+ Class> clazz = bb.getClass();
+ try {
+ freeMethod = clazz.getMethod("free");
+ } catch (NoSuchMethodException ex) {
+ } catch (SecurityException ex) {
+ }
+ }
+
+ private static Method loadMethod(String className, String methodName) {
+ try {
+ Method method = Class.forName(className).getMethod(methodName);
+ method.setAccessible(true);
+ return method;
+ } catch (NoSuchMethodException ex) {
+ return null; // the method was not found
+ } catch (SecurityException ex) {
+ return null; // setAccessible not allowed by security policy
+ } catch (ClassNotFoundException ex) {
+ return null; // the direct buffer implementation was not found
+ }
+ }
+
+ @Override
+ /**
+ * This function explicitly calls the Cleaner method of a direct buffer.
+ *
+ * @param toBeDestroyed
+ * The direct buffer that will be "cleaned". Utilizes reflection.
+ *
+ */
+ public void destroyDirectBuffer(Buffer toBeDestroyed) {
+ try {
+ if (freeMethod != null) {
+ freeMethod.invoke(toBeDestroyed);
+ } else {
+ Object cleaner = cleanerMethod.invoke(toBeDestroyed);
+ if (cleaner != null) {
+ cleanMethod.invoke(cleaner);
+ } else {
+ // Try the alternate approach of getting the viewed buffer
+ // first
+ Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);
+ if (viewedBuffer != null) {
+ destroyDirectBuffer((Buffer) viewedBuffer);
+ } else {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE,
+ "Buffer cannot be destroyed: {0}", toBeDestroyed);
+ }
+ }
+ }
+ } catch (IllegalAccessException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ } catch (IllegalArgumentException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ } catch (InvocationTargetException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ } catch (SecurityException ex) {
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
+ }
+ }
+
+ @Override
+ public ByteBuffer allocate(int size) {
+ return ByteBuffer.allocateDirect(size);
+ }
+
+}