aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar nmittler <nathanmittler@google.com>2016-10-20 10:11:44 -0700
committerGravatar nmittler <nathanmittler@google.com>2016-10-20 11:25:11 -0700
commitc0f95c6f942a8d269c9d432956ad9babb900df7e (patch)
treeb08cf8027d4fde833307c95091dd718617620d37
parent9c6940f3bb4d7893b4d453870acd374c1ae43a58 (diff)
Hacking ByteBufferWriter to work with GAE
Fixes #2269
-rw-r--r--java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java50
1 files changed, 45 insertions, 5 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java b/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
index 0cc38175..6157a52f 100644
--- a/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
+++ b/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
@@ -33,11 +33,12 @@ package com.google.protobuf;
import static java.lang.Math.max;
import static java.lang.Math.min;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
/**
* Utility class to provide efficient writing of {@link ByteBuffer}s to {@link OutputStream}s.
@@ -75,6 +76,12 @@ final class ByteBufferWriter {
new ThreadLocal<SoftReference<byte[]>>();
/**
+ * This is a hack for GAE, where {@code FileOutputStream} is unavailable.
+ */
+ private static final Class<?> FILE_OUTPUT_STREAM_CLASS = safeGetClass("java.io.FileOutputStream");
+ private static final long CHANNEL_FIELD_OFFSET = getChannelFieldOffset(FILE_OUTPUT_STREAM_CLASS);
+
+ /**
* For testing purposes only. Clears the cached buffer to force a new allocation on the next
* invocation.
*/
@@ -93,10 +100,7 @@ final class ByteBufferWriter {
// Optimized write for array-backed buffers.
// Note that we're taking the risk that a malicious OutputStream could modify the array.
output.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
- } else if (output instanceof FileOutputStream) {
- // Use a channel to write out the ByteBuffer. This will automatically empty the buffer.
- ((FileOutputStream) output).getChannel().write(buffer);
- } else {
+ } else if (!writeToChannel(buffer, output)){
// Read all of the data from the buffer to an array.
// TODO(nathanmittler): Consider performance improvements for other "known" stream types.
final byte[] array = getOrCreateBuffer(buffer.remaining());
@@ -142,4 +146,40 @@ final class ByteBufferWriter {
private static void setBuffer(byte[] value) {
BUFFER.set(new SoftReference<byte[]>(value));
}
+
+ private static boolean writeToChannel(ByteBuffer buffer, OutputStream output) throws IOException {
+ if (CHANNEL_FIELD_OFFSET >= 0 && FILE_OUTPUT_STREAM_CLASS.isInstance(output)) {
+ // Use a channel to write out the ByteBuffer. This will automatically empty the buffer.
+ WritableByteChannel channel = null;
+ try {
+ channel = (WritableByteChannel) UnsafeUtil.getObject(output, CHANNEL_FIELD_OFFSET);
+ } catch (ClassCastException e) {
+ // Absorb.
+ }
+ if (channel != null) {
+ channel.write(buffer);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static Class<?> safeGetClass(String className) {
+ try {
+ return Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+ private static long getChannelFieldOffset(Class<?> clazz) {
+ try {
+ if (clazz != null && UnsafeUtil.hasUnsafeArrayOperations()) {
+ Field field = clazz.getDeclaredField("channel");
+ return UnsafeUtil.objectFieldOffset(field);
+ }
+ } catch (Throwable e) {
+ // Absorb
+ }
+ return -1;
+ }
}