You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by sc...@apache.org on 2013/04/30 18:18:26 UTC
svn commit: r1477712 [1/2] - in /avro/trunk: ./
lang/java/avro/src/main/java/org/apache/avro/generic/
lang/java/avro/src/main/java/org/apache/avro/io/
lang/java/avro/src/main/java/org/apache/avro/reflect/
lang/java/avro/src/test/java/org/apache/avro/re...
Author: scottcarey
Date: Tue Apr 30 16:18:26 2013
New Revision: 1477712
URL: http://svn.apache.org/r1477712
Log:
AVRO-1282. Java: Use sun.misc.Unsafe to improve Reflect API Performance.
(Leo Romanoff via scottcarey)
Added:
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java
Modified:
avro/trunk/CHANGES.txt
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java
avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java
avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java
avro/trunk/lang/java/ipc/src/test/java/org/apache/avro/io/Perf.java
Modified: avro/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Tue Apr 30 16:18:26 2013
@@ -39,6 +39,9 @@ Trunk (not yet released)
AVRO-1299. Java: SpecificRecordBase implements GenericRecord.
(Christophe Taton via cutting)
+ AVRO-1282. Java: Use sun.misc.Unsafe to improve Reflect API Performance.
+ (Leo Romanoff via scottcarey)
+
BUG FIXES
AVRO-1296. Python: Fix schemas retrieved from protocol types
Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumReader.java Tue Apr 30 16:18:26 2013
@@ -165,7 +165,7 @@ public class GenericDatumReader<D> imple
default: throw new AvroRuntimeException("Unknown type: " + expected);
}
}
-
+
/** Called to read a record instance. May be overridden for alternate record
* representations.*/
protected Object readRecord(Object old, Schema expected,
@@ -176,13 +176,23 @@ public class GenericDatumReader<D> imple
for (Field f : in.readFieldOrder()) {
int pos = f.pos();
String name = f.name();
- Object oldDatum = (old!=null) ? data.getField(r, name, pos, state) : null;
- data.setField(r, name, pos, read(oldDatum, f.schema(), in), state);
+ Object oldDatum = null;
+ if (old!=null) {
+ oldDatum = data.getField(r, name, pos, state);
+ }
+ readField(r, f, oldDatum, in, state);
}
return r;
}
+ /** Called to read a single field of a record. May be overridden for more
+ * efficient or alternate implementations.*/
+ protected void readField(Object r, Field f, Object oldDatum,
+ ResolvingDecoder in, Object state) throws IOException {
+ data.setField(r, f.name(), f.pos(), read(oldDatum, f.schema(), in), state);
+ }
+
/** Called to read an enum value. May be overridden for alternate enum
* representations. By default, returns a GenericEnumSymbol. */
protected Object readEnum(Schema expected, Decoder in) throws IOException {
Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/generic/GenericDatumWriter.java Tue Apr 30 16:18:26 2013
@@ -101,12 +101,19 @@ public class GenericDatumWriter<D> imple
throws IOException {
Object state = data.getRecordState(datum, schema);
for (Field f : schema.getFields()) {
- Object value = data.getField(datum, f.name(), f.pos(), state);
- try {
- write(f.schema(), value, out);
- } catch (NullPointerException e) {
- throw npe(e, " in field "+f.name());
- }
+ writeField(datum, f, out, state);
+ }
+ }
+
+ /** Called to write a single field of a record. May be overridden for more
+ * efficient or alternate implementations.*/
+ protected void writeField(Object datum, Field f, Encoder out, Object state)
+ throws IOException {
+ Object value = data.getField(datum, f.name(), f.pos(), state);
+ try {
+ write(f.schema(), value, out);
+ } catch (NullPointerException e) {
+ throw npe(e, " in field " + f.name());
}
}
Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/EncoderFactory.java Tue Apr 30 16:18:26 2013
@@ -119,7 +119,7 @@ public class EncoderFactory {
size = MIN_BLOCK_BUFFER_SIZE;
if (size > MAX_BLOCK_BUFFER_SIZE)
size = MAX_BLOCK_BUFFER_SIZE;
- this.binaryBufferSize = size;
+ this.binaryBlockSize = size;
return this;
}
@@ -249,11 +249,15 @@ public class EncoderFactory {
* @see BlockingBinaryEncoder
* @see Encoder
*/
- public BinaryEncoder blockingBinaryEncoder(OutputStream out, BinaryEncoder reuse) {
+ public BinaryEncoder blockingBinaryEncoder(OutputStream out,
+ BinaryEncoder reuse) {
+ int blockSize = this.binaryBlockSize;
+ int bufferSize = (blockSize * 2 >= this.binaryBufferSize) ? 32
+ : this.binaryBufferSize;
if (null == reuse || !reuse.getClass().equals(BlockingBinaryEncoder.class)) {
- return new BlockingBinaryEncoder(out, this.binaryBlockSize, 32);
+ return new BlockingBinaryEncoder(out, blockSize, bufferSize);
} else {
- return ((BlockingBinaryEncoder)reuse).configure(out, this.binaryBlockSize, 32);
+ return ((BlockingBinaryEncoder) reuse).configure(out, blockSize, bufferSize);
}
}
Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ArrayAccessor.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,228 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.avro.io.Encoder;
+import org.apache.avro.io.ResolvingDecoder;
+
+/**
+ * Helper class to provide native array access whenever possible. It is much
+ * faster than using reflection-based operations on arrays.
+ */
+class ArrayAccessor {
+
+ static void writeArray(boolean[] data, Encoder out) throws IOException {
+ int size = data.length;
+ out.setItemCount(size);
+ for (int i = 0; i < size; i++) {
+ out.startItem();
+ out.writeBoolean(data[i]);
+ }
+ }
+
+ // short, and char arrays are upcast to avro int
+
+ static void writeArray(short[] data, Encoder out) throws IOException {
+ int size = data.length;
+ out.setItemCount(size);
+ for (int i = 0; i < size; i++) {
+ out.startItem();
+ out.writeInt(data[i]);
+ }
+ }
+
+ static void writeArray(char[] data, Encoder out) throws IOException {
+ int size = data.length;
+ out.setItemCount(size);
+ for (int i = 0; i < size; i++) {
+ out.startItem();
+ out.writeInt(data[i]);
+ }
+ }
+
+ static void writeArray(int[] data, Encoder out) throws IOException {
+ int size = data.length;
+ out.setItemCount(size);
+ for (int i = 0; i < size; i++) {
+ out.startItem();
+ out.writeInt(data[i]);
+ }
+ }
+
+ static void writeArray(long[] data, Encoder out) throws IOException {
+ int size = data.length;
+ out.setItemCount(size);
+ for (int i = 0; i < size; i++) {
+ out.startItem();
+ out.writeLong(data[i]);
+ }
+ }
+
+ static void writeArray(float[] data, Encoder out) throws IOException {
+ int size = data.length;
+ out.setItemCount(size);
+ for (int i = 0; i < size; i++) {
+ out.startItem();
+ out.writeFloat(data[i]);
+ }
+ }
+
+ static void writeArray(double[] data, Encoder out) throws IOException {
+ int size = data.length;
+ out.setItemCount(size);
+ for (int i = 0; i < size; i++) {
+ out.startItem();
+ out.writeDouble(data[i]);
+ }
+ }
+
+ static Object readArray(Object array, Class<?> elementType, long l,
+ ResolvingDecoder in) throws IOException {
+ if (elementType == int.class)
+ return readArray((int[]) array, l, in);
+ if (elementType == long.class)
+ return readArray((long[]) array, l, in);
+ if (elementType == float.class)
+ return readArray((float[]) array, l, in);
+ if (elementType == double.class)
+ return readArray((double[]) array, l, in);
+ if (elementType == boolean.class)
+ return readArray((boolean[]) array, l, in);
+ if (elementType == char.class)
+ return readArray((char[]) array, l, in);
+ if (elementType == short.class)
+ return readArray((short[]) array, l, in);
+ return null;
+ }
+
+ static boolean[] readArray(boolean[] array, long l, ResolvingDecoder in)
+ throws IOException {
+ int index = 0;
+ do {
+ int limit = index + (int) l;
+ if (array.length < limit) {
+ array = Arrays.copyOf(array, limit);
+ }
+ while (index < limit) {
+ array[index] = in.readBoolean();
+ index++;
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return array;
+ }
+
+ static int[] readArray(int[] array, long l, ResolvingDecoder in)
+ throws IOException {
+ int index = 0;
+ do {
+ int limit = index + (int) l;
+ if (array.length < limit) {
+ array = Arrays.copyOf(array, limit);
+ }
+ while (index < limit) {
+ array[index] = in.readInt();
+ index++;
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return array;
+ }
+
+ static short[] readArray(short[] array, long l, ResolvingDecoder in)
+ throws IOException {
+ int index = 0;
+ do {
+ int limit = index + (int) l;
+ if (array.length < limit) {
+ array = Arrays.copyOf(array, limit);
+ }
+ while (index < limit) {
+ array[index] = (short) in.readInt();
+ index++;
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return array;
+ }
+
+ static char[] readArray(char[] array, long l, ResolvingDecoder in)
+ throws IOException {
+ int index = 0;
+ do {
+ int limit = index + (int) l;
+ if (array.length < limit) {
+ array = Arrays.copyOf(array, limit);
+ }
+ while (index < limit) {
+ array[index] = (char) in.readInt();
+ index++;
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return array;
+ }
+
+ static long[] readArray(long[] array, long l, ResolvingDecoder in)
+ throws IOException {
+ int index = 0;
+ do {
+ int limit = index + (int) l;
+ if (array.length < limit) {
+ array = Arrays.copyOf(array, limit);
+ }
+ while (index < limit) {
+ array[index] = in.readLong();
+ index++;
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return array;
+ }
+
+ static float[] readArray(float[] array, long l, ResolvingDecoder in)
+ throws IOException {
+ int index = 0;
+ do {
+ int limit = index + (int) l;
+ if (array.length < limit) {
+ array = Arrays.copyOf(array, limit);
+ }
+ while (index < limit) {
+ array[index] = in.readFloat();
+ index++;
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return array;
+ }
+
+ static double[] readArray(double[] array, long l, ResolvingDecoder in)
+ throws IOException {
+ int index = 0;
+ do {
+ int limit = index + (int) l;
+ if (array.length < limit) {
+ array = Arrays.copyOf(array, limit);
+ }
+ while (index < limit) {
+ array[index] = in.readDouble();
+ index++;
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return array;
+ }
+
+}
Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccess.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.lang.reflect.Field;
+
+abstract class FieldAccess {
+
+ protected abstract FieldAccessor getAccessor(Field field);
+
+}
Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessReflect.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+class FieldAccessReflect extends FieldAccess {
+
+ @Override
+ protected FieldAccessor getAccessor(Field field) {
+ return new ReflectionBasedAccessor(field);
+ }
+
+ private final class ReflectionBasedAccessor extends FieldAccessor {
+ private final Field field;
+
+ public ReflectionBasedAccessor(Field field) {
+ this.field = field;
+ }
+
+ @Override
+ public String toString() {
+ return field.getName();
+ }
+
+ @Override
+ public Object get(Object object) throws IllegalAccessException {
+ return field.get(object);
+ }
+
+ @Override
+ public void set(Object object, Object value) throws IllegalAccessException,
+ IOException {
+ field.set(object, value);
+ }
+
+ }
+}
Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessUnsafe.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,309 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.apache.avro.io.Decoder;
+import org.apache.avro.io.Encoder;
+
+import sun.misc.Unsafe;
+
+@SuppressWarnings("restriction")
+class FieldAccessUnsafe extends FieldAccess {
+
+ private static final Unsafe UNSAFE;
+
+ static {
+ try {
+ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ UNSAFE = (Unsafe) theUnsafe.get(null);
+ // It seems not all Unsafe implementations implement the following method.
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected FieldAccessor getAccessor(Field field) {
+ Class<?> c = field.getType();
+ if (c == int.class)
+ return new UnsafeIntField(field);
+ else if (c == long.class)
+ return new UnsafeLongField(field);
+ else if (c == byte.class)
+ return new UnsafeByteField(field);
+ else if (c == float.class)
+ return new UnsafeFloatField(field);
+ else if (c == double.class)
+ return new UnsafeDoubleField(field);
+ else if (c == char.class)
+ return new UnsafeCharField(field);
+ else if (c == boolean.class)
+ return new UnsafeBooleanField(field);
+ else if (c == short.class)
+ return new UnsafeShortField(field);
+ else
+ return new UnsafeObjectField(field);
+ }
+
+ abstract static class UnsafeCachedField extends FieldAccessor {
+ protected final long offset;
+
+ UnsafeCachedField(long offset) {
+ this.offset = offset;
+ }
+
+ @Override
+ protected boolean supportsIO() {
+ return true;
+ }
+ }
+
+ final static class UnsafeIntField extends UnsafeCachedField {
+ UnsafeIntField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putInt(object, offset, (Integer) value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getInt(object, offset);
+ }
+
+ @Override
+ protected void read(Object object, Decoder in) throws IOException {
+ UNSAFE.putInt(object, offset, in.readInt());
+ }
+
+ @Override
+ protected void write(Object object, Encoder out) throws IOException {
+ out.writeInt(UNSAFE.getInt(object, offset));
+ }
+ }
+
+ final static class UnsafeFloatField extends UnsafeCachedField {
+ protected UnsafeFloatField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putFloat(object, offset, (Float) value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getFloat(object, offset);
+ }
+
+ @Override
+ protected void read(Object object, Decoder in) throws IOException {
+ UNSAFE.putFloat(object, offset, in.readFloat());
+ }
+
+ @Override
+ protected void write(Object object, Encoder out) throws IOException {
+ out.writeFloat(UNSAFE.getFloat(object, offset));
+ }
+ }
+
+ final static class UnsafeShortField extends UnsafeCachedField {
+ protected UnsafeShortField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putShort(object, offset, (Short) value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getShort(object, offset);
+ }
+
+ @Override
+ protected void read(Object object, Decoder in) throws IOException {
+ UNSAFE.putShort(object, offset, (short) in.readInt());
+ }
+
+ @Override
+ protected void write(Object object, Encoder out) throws IOException {
+ out.writeInt(UNSAFE.getShort(object, offset));
+ }
+ }
+
+ final static class UnsafeByteField extends UnsafeCachedField {
+ protected UnsafeByteField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putByte(object, offset, (Byte) value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getByte(object, offset);
+ }
+
+ @Override
+ protected void read(Object object, Decoder in) throws IOException {
+ UNSAFE.putByte(object, offset, (byte) in.readInt());
+ }
+
+ @Override
+ protected void write(Object object, Encoder out) throws IOException {
+ out.writeInt(UNSAFE.getByte(object, offset));
+ }
+ }
+
+ final static class UnsafeBooleanField extends UnsafeCachedField {
+ protected UnsafeBooleanField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putBoolean(object, offset, (Boolean) value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getBoolean(object, offset);
+ }
+
+ @Override
+ protected void read(Object object, Decoder in) throws IOException {
+ UNSAFE.putBoolean(object, offset, in.readBoolean());
+ }
+
+ @Override
+ protected void write(Object object, Encoder out) throws IOException {
+ out.writeBoolean(UNSAFE.getBoolean(object, offset));
+ }
+ }
+
+ final static class UnsafeCharField extends UnsafeCachedField {
+ protected UnsafeCharField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putInt(object, offset, (Character) value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getChar(object, offset);
+ }
+
+ @Override
+ protected void read(Object object, Decoder in) throws IOException {
+ UNSAFE.putChar(object, offset, (char) in.readInt());
+ }
+
+ @Override
+ protected void write(Object object, Encoder out) throws IOException {
+ out.writeInt(UNSAFE.getChar(object, offset));
+ }
+ }
+
+ final static class UnsafeLongField extends UnsafeCachedField {
+ protected UnsafeLongField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putLong(object, offset, (Long) value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getLong(object, offset);
+ }
+
+ @Override
+ protected void read(Object object, Decoder in) throws IOException {
+ UNSAFE.putLong(object, offset, in.readLong());
+ }
+
+ @Override
+ protected void write(Object object, Encoder out) throws IOException {
+ out.writeLong(UNSAFE.getLong(object, offset));
+ }
+ }
+
+ final static class UnsafeDoubleField extends UnsafeCachedField {
+ protected UnsafeDoubleField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putDouble(object, offset, (Double) value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getDouble(object, offset);
+ }
+
+ @Override
+ protected void read(Object object, Decoder in) throws IOException {
+ UNSAFE.putDouble(object, offset, in.readDouble());
+ }
+
+ @Override
+ protected void write(Object object, Encoder out) throws IOException {
+ out.writeDouble(UNSAFE.getDouble(object, offset));
+ }
+ }
+
+ final static class UnsafeObjectField extends UnsafeCachedField {
+ protected UnsafeObjectField(Field f) {
+ super(UNSAFE.objectFieldOffset(f));
+ }
+
+ @Override
+ protected void set(Object object, Object value) {
+ UNSAFE.putObject(object, offset, value);
+ }
+
+ @Override
+ protected Object get(Object object) {
+ return UNSAFE.getObject(object, offset);
+ }
+
+ @Override
+ protected boolean supportsIO() {
+ return false;
+ }
+
+ }
+}
Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/FieldAccessor.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+
+import org.apache.avro.io.Decoder;
+import org.apache.avro.io.Encoder;
+
+abstract class FieldAccessor {
+ FieldAccessor() {
+ }
+
+ protected abstract Object get(Object object) throws IllegalAccessException;
+
+ protected abstract void set(Object object, Object value)
+ throws IllegalAccessException, IOException;
+
+ protected void read(Object object, Decoder in) throws IOException {
+ }
+
+ protected void write(Object object, Encoder out) throws IOException {
+ }
+
+ protected boolean supportsIO() {
+ return false;
+ }
+}
Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java Tue Apr 30 16:18:26 2013
@@ -17,36 +17,39 @@
*/
package org.apache.avro.reflect;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.GenericArrayType;
-import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.avro.AvroRemoteException;
import org.apache.avro.AvroRuntimeException;
import org.apache.avro.AvroTypeException;
import org.apache.avro.Protocol;
-import org.apache.avro.Schema;
import org.apache.avro.Protocol.Message;
-import org.apache.avro.generic.IndexedRecord;
-import org.apache.avro.generic.GenericFixed;
+import org.apache.avro.Schema;
import org.apache.avro.generic.GenericContainer;
-import org.apache.avro.specific.SpecificData;
-import org.apache.avro.specific.FixedSize;
+import org.apache.avro.generic.GenericFixed;
+import org.apache.avro.generic.IndexedRecord;
import org.apache.avro.io.BinaryData;
import org.apache.avro.io.DatumReader;
+import org.apache.avro.specific.FixedSize;
+import org.apache.avro.specific.SpecificData;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.NullNode;
@@ -55,7 +58,6 @@ import com.thoughtworks.paranamer.Parana
/** Utilities to use existing Java classes and interfaces via reflection. */
public class ReflectData extends SpecificData {
-
/** {@link ReflectData} implementation that permits null field values. The
* schema generated for each field is a union of its declared type and
* null. */
@@ -66,6 +68,7 @@ public class ReflectData extends Specifi
/** Return the singleton instance. */
public static AllowNull get() { return INSTANCE; }
+ @Override
protected Schema createFieldSchema(Field field, Map<String, Schema> names) {
Schema schema = super.createFieldSchema(field, names);
return makeNullable(schema);
@@ -98,27 +101,49 @@ public class ReflectData extends Specifi
@Override
public void setField(Object record, String name, int position, Object o) {
+ setField(record, name, position, o, null);
+ }
+
+ @Override
+ protected void setField(Object record, String name, int pos, Object o,
+ Object state) {
if (record instanceof IndexedRecord) {
- super.setField(record, name, position, o);
+ super.setField(record, name, pos, o);
return;
}
try {
- getField(record.getClass(), name).set(record, o);
+ getAccessorForField(record, name, pos, state).set(record, o);
} catch (IllegalAccessException e) {
throw new AvroRuntimeException(e);
+ } catch (IOException e) {
+ throw new AvroRuntimeException(e);
}
}
@Override
public Object getField(Object record, String name, int position) {
- if (record instanceof IndexedRecord)
- return super.getField(record, name, position);
+ return getField(record, name, position, null);
+ }
+
+ @Override
+ protected Object getField(Object record, String name, int pos, Object state) {
+ if (record instanceof IndexedRecord) {
+ return super.getField(record, name, pos);
+ }
try {
- return getField(record.getClass(), name).get(record);
+ return getAccessorForField(record, name, pos, state).get(record);
} catch (IllegalAccessException e) {
throw new AvroRuntimeException(e);
}
}
+
+ private FieldAccessor getAccessorForField(Object record, String name,
+ int pos, Object optionalState) {
+ if (optionalState != null) {
+ return ((FieldAccessor[])optionalState)[pos];
+ }
+ return getFieldAccessor(record.getClass(), name);
+ }
@Override
protected boolean isRecord(Object datum) {
@@ -166,37 +191,83 @@ public class ReflectData extends Specifi
return super.validate(schema, datum);
}
}
+
+ private static final ConcurrentHashMap<Class<?>, ClassAccessorData>
+ ACCESSOR_CACHE = new ConcurrentHashMap<Class<?>, ClassAccessorData>();
- private static final Map<Class,Map<String,Field>> FIELD_CACHE =
- new ConcurrentHashMap<Class,Map<String,Field>>();
+ private static class ClassAccessorData {
+ private final Class<?> clazz;
+ private final Map<String, FieldAccessor> byName =
+ new HashMap<String, FieldAccessor>();
+ private final IdentityHashMap<Schema, FieldAccessor[]> bySchema =
+ new IdentityHashMap<Schema, FieldAccessor[]>();
+
+ private ClassAccessorData(Class<?> c) {
+ clazz = c;
+ for(Field f : getFields(c, false)) {
+ FieldAccessor accessor = ReflectionUtil.getFieldAccess().getAccessor(f);
+ byName.put(f.getName(), accessor);
+ }
+ }
+
+ /**
+ * Return the field accessors as an array, indexed by the field
+ * index of the given schema.
+ */
+ private synchronized FieldAccessor[] getAccessorsFor(Schema schema) {
+ FieldAccessor[] result = bySchema.get(schema);
+ if (result == null) {
+ result = createAccessorsFor(schema);
+ bySchema.put(schema, result);
+ }
+ return result;
+ }
- /** Return the named field of the provided class. Implementation caches
- * values, since this is used at runtime to get and set fields. */
- private static Field getField(Class c, String name) {
- Map<String,Field> fields = FIELD_CACHE.get(c);
- if (fields == null) {
- fields = new ConcurrentHashMap<String,Field>();
- FIELD_CACHE.put(c, fields);
- }
- Field f = fields.get(name);
- if (f == null) {
- f = findField(c, name);
- fields.put(name, f);
+ private FieldAccessor[] createAccessorsFor(Schema schema) {
+ List<Schema.Field> avroFields = schema.getFields();
+ FieldAccessor[] result = new FieldAccessor[avroFields.size()];
+ for(Schema.Field avroField : schema.getFields()) {
+ result[avroField.pos()] = byName.get(avroField.name());
+ }
+ return result;
}
- return f;
- }
- private static Field findField(Class original, String name) {
- Class c = original;
- do {
- try {
- Field f = c.getDeclaredField(name);
- f.setAccessible(true);
- return f;
- } catch (NoSuchFieldException e) {}
- c = c.getSuperclass();
- } while (c != null);
- throw new AvroRuntimeException("No field named "+name+" in: "+original);
+ private FieldAccessor getAccessorFor(String fieldName) {
+ FieldAccessor result = byName.get(fieldName);
+ if (result == null) {
+ throw new AvroRuntimeException(
+ "No field named " + fieldName + " in: " + clazz);
+ }
+ return result;
+ }
+ }
+
+ private ClassAccessorData getClassAccessorData(Class<?> c) {
+ ClassAccessorData data = ACCESSOR_CACHE.get(c);
+ if(data == null && !IndexedRecord.class.isAssignableFrom(c)){
+ ClassAccessorData newData = new ClassAccessorData(c);
+ data = ACCESSOR_CACHE.putIfAbsent(c, newData);
+ if (null == data) {
+ data = newData;
+ }
+ }
+ return data;
+ }
+
+ private FieldAccessor[] getFieldAccessors(Class<?> c, Schema s) {
+ ClassAccessorData data = getClassAccessorData(c);
+ if (data != null) {
+ return data.getAccessorsFor(s);
+ }
+ return null;
+ }
+
+ private FieldAccessor getFieldAccessor(Class<?> c, String fieldName) {
+ ClassAccessorData data = getClassAccessorData(c);
+ if (data != null) {
+ return data.getAccessorFor(fieldName);
+ }
+ return null;
}
/** @deprecated Replaced by {@link SpecificData#CLASS_PROP} */
@@ -209,17 +280,37 @@ public class ReflectData extends Specifi
@Deprecated
static final String ELEMENT_PROP = "java-element-class";
+ private static final Map<String,Class> CLASS_CACHE =
+ new ConcurrentHashMap<String, Class>();
+
static Class getClassProp(Schema schema, String prop) {
String name = schema.getProp(prop);
if (name == null) return null;
+ Class c = CLASS_CACHE.get(name);
+ if (c != null)
+ return c;
try {
- return Class.forName(name);
+ c = Class.forName(name);
+ CLASS_CACHE.put(name, c);
} catch (ClassNotFoundException e) {
throw new AvroRuntimeException(e);
}
+ return c;
}
private static final Class BYTES_CLASS = new byte[0].getClass();
+ private static final IdentityHashMap<Class, Class> ARRAY_CLASSES;
+ static {
+ ARRAY_CLASSES = new IdentityHashMap<Class, Class>();
+ ARRAY_CLASSES.put(byte.class, byte[].class);
+ ARRAY_CLASSES.put(char.class, char[].class);
+ ARRAY_CLASSES.put(short.class, short[].class);
+ ARRAY_CLASSES.put(int.class, int[].class);
+ ARRAY_CLASSES.put(long.class, long[].class);
+ ARRAY_CLASSES.put(float.class, float[].class);
+ ARRAY_CLASSES.put(double.class, double[].class);
+ ARRAY_CLASSES.put(boolean.class, boolean[].class);
+ }
@Override
public Class getClass(Schema schema) {
@@ -228,7 +319,13 @@ public class ReflectData extends Specifi
Class collectionClass = getClassProp(schema, CLASS_PROP);
if (collectionClass != null)
return collectionClass;
- return java.lang.reflect.Array.newInstance(getClass(schema.getElementType()),0).getClass();
+ Class elementClass = getClass(schema.getElementType());
+ if(elementClass.isPrimitive()) {
+ // avoid expensive translation to array type when primitive
+ return ARRAY_CLASSES.get(elementClass);
+ } else {
+ return java.lang.reflect.Array.newInstance(elementClass, 0).getClass();
+ }
case STRING:
Class stringClass = getClassProp(schema, CLASS_PROP);
if (stringClass != null)
@@ -239,13 +336,13 @@ public class ReflectData extends Specifi
String intClass = schema.getProp(CLASS_PROP);
if (Byte.class.getName().equals(intClass)) return Byte.TYPE;
if (Short.class.getName().equals(intClass)) return Short.TYPE;
+ if (Character.class.getName().equals(intClass)) return Character.TYPE;
default:
return super.getClass(schema);
}
}
@Override
- @SuppressWarnings(value="unchecked")
protected Schema createSchema(Type type, Map<String,Schema> names) {
if (type instanceof GenericArrayType) { // generic array
Type component = ((GenericArrayType)type).getGenericComponentType();
@@ -282,6 +379,10 @@ public class ReflectData extends Specifi
Schema result = Schema.create(Schema.Type.INT);
result.addProp(CLASS_PROP, Short.class.getName());
return result;
+ } else if ((type == Character.class) || (type == Character.TYPE)) {
+ Schema result = Schema.create(Schema.Type.INT);
+ result.addProp(CLASS_PROP, Character.class.getName());
+ return result;
} else if (type instanceof Class) { // Class
Class<?> c = (Class<?>)type;
if (c.isPrimitive() || // primitives
@@ -339,7 +440,7 @@ public class ReflectData extends Specifi
boolean error = Throwable.class.isAssignableFrom(c);
schema = Schema.createRecord(name, null /* doc */, space, error);
names.put(c.getName(), schema);
- for (Field field : getFields(c))
+ for (Field field : getCachedFields(c))
if ((field.getModifiers()&(Modifier.TRANSIENT|Modifier.STATIC))==0){
Schema fieldSchema = createFieldSchema(field, names);
JsonNode defaultValue = null;
@@ -349,8 +450,9 @@ public class ReflectData extends Specifi
defaultValue = NullNode.getInstance();
}
}
- fields.add(new Schema.Field(field.getName(),
- fieldSchema, null /* doc */, defaultValue));
+ Schema.Field recordField = new Schema.Field(field.getName(),
+ fieldSchema, null /* doc */, defaultValue);
+ fields.add(recordField);
}
if (error) // add Throwable message
fields.add(new Schema.Field("detailMessage", THROWABLE_MESSAGE,
@@ -373,7 +475,6 @@ public class ReflectData extends Specifi
// if array element type is a class with a union annotation, note it
// this is required because we cannot set a property on the union itself
- @SuppressWarnings(value="unchecked")
private void setElement(Schema schema, Type element) {
if (!(element instanceof Class)) return;
Class<?> c = (Class<?>)element;
@@ -396,24 +497,37 @@ public class ReflectData extends Specifi
schema));
}
+ private static final Map<Class<?>,Field[]> FIELDS_CACHE =
+ new ConcurrentHashMap<Class<?>,Field[]>();
+
// Return of this class and its superclasses to serialize.
- // Not cached, since this is only used to create schemas, which are cached.
- private Collection<Field> getFields(Class recordClass) {
+ private static Field[] getCachedFields(Class<?> recordClass) {
+ Field[] fieldsList = FIELDS_CACHE.get(recordClass);
+ if (fieldsList != null)
+ return fieldsList;
+ fieldsList = getFields(recordClass, true);
+ FIELDS_CACHE.put(recordClass, fieldsList);
+ return fieldsList;
+ }
+
+ private static Field[] getFields(Class<?> recordClass, boolean excludeJava) {
+ Field[] fieldsList;
Map<String,Field> fields = new LinkedHashMap<String,Field>();
- Class c = recordClass;
+ Class<?> c = recordClass;
do {
- if (c.getPackage() != null
+ if (excludeJava && c.getPackage() != null
&& c.getPackage().getName().startsWith("java."))
- break; // skip java built-in classes
+ break; // skip java built-in classes
for (Field field : c.getDeclaredFields())
if ((field.getModifiers() & (Modifier.TRANSIENT|Modifier.STATIC)) == 0)
if (fields.put(field.getName(), field) != null)
throw new AvroTypeException(c+" contains two fields named: "+field);
c = c.getSuperclass();
} while (c != null);
- return fields.values();
+ fieldsList = fields.values().toArray(new Field[0]);
+ return fieldsList;
}
-
+
/** Create a schema for a field. */
protected Schema createFieldSchema(Field field, Map<String, Schema> names) {
Schema schema = createSchema(field.getGenericType(), names);
@@ -487,7 +601,6 @@ public class ReflectData extends Specifi
if (err != AvroRemoteException.class)
errs.add(getSchema(err, names));
Schema errors = Schema.createUnion(errs);
-
return protocol.createMessage(method.getName(), null /* doc */, request, response, errors);
}
@@ -527,4 +640,8 @@ public class ReflectData extends Specifi
return super.compare(o1, o2, s, equals);
}
+ @Override
+ protected Object getRecordState(Object record, Schema schema) {
+ return getFieldAccessors(record.getClass(), schema);
+ }
}
Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumReader.java Tue Apr 30 16:18:26 2013
@@ -18,14 +18,18 @@
package org.apache.avro.reflect;
import java.io.IOException;
-import java.util.Collection;
-import java.util.ArrayList;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import org.apache.avro.AvroRuntimeException;
import org.apache.avro.Schema;
-import org.apache.avro.specific.SpecificDatumReader;
+import org.apache.avro.Schema.Field;
import org.apache.avro.io.Decoder;
+import org.apache.avro.io.ResolvingDecoder;
+import org.apache.avro.specific.SpecificData;
+import org.apache.avro.specific.SpecificDatumReader;
/**
* {@link org.apache.avro.io.DatumReader DatumReader} for existing classes via
@@ -63,29 +67,29 @@ public class ReflectDatumReader<T> exten
}
@Override
- @SuppressWarnings(value="unchecked")
protected Object newArray(Object old, int size, Schema schema) {
- Class collectionClass =
- ReflectData.getClassProp(schema, ReflectData.CLASS_PROP);
- Class elementClass =
- ReflectData.getClassProp(schema, ReflectData.ELEMENT_PROP);
+ Class<?> collectionClass =
+ ReflectData.getClassProp(schema, SpecificData.CLASS_PROP);
+ Class<?> elementClass =
+ ReflectData.getClassProp(schema, SpecificData.ELEMENT_PROP);
if (collectionClass == null && elementClass == null)
return super.newArray(old, size, schema); // use specific/generic
- ReflectData data = (ReflectData)getData();
if (collectionClass != null && !collectionClass.isArray()) {
if (old instanceof Collection) {
- ((Collection)old).clear();
+ ((Collection<?>)old).clear();
return old;
}
if (collectionClass.isAssignableFrom(ArrayList.class))
- return new ArrayList();
- return data.newInstance(collectionClass, schema);
+ return new ArrayList<Object>();
+ return SpecificData.newInstance(collectionClass, schema);
}
- if (elementClass == null)
+ if (elementClass == null) {
+ ReflectData data = (ReflectData)getData();
elementClass = data.getClass(schema.getElementType());
+ }
return Array.newInstance(elementClass, size);
}
@@ -95,15 +99,70 @@ public class ReflectDatumReader<T> exten
}
@Override
- @SuppressWarnings(value="unchecked")
protected void addToArray(Object array, long pos, Object e) {
+ throw new AvroRuntimeException("reflectDatumReader does not use addToArray");
+ }
+
+ @Override
+ /** Called to read an array instance. May be overridden for alternate array
+ * representations.*/
+ protected Object readArray(Object old, Schema expected, ResolvingDecoder in)
+ throws IOException {
+ Schema expectedType = expected.getElementType();
+ long l = in.readArrayStart();
+ if (l <= 0) {
+ return newArray(old, 0, expected);
+ }
+ Object array = newArray(old, (int) l, expected);
if (array instanceof Collection) {
- ((Collection)array).add(e);
+ @SuppressWarnings("unchecked")
+ Collection<Object> c = (Collection<Object>) array;
+ return readCollection(c, expectedType, l, in);
} else {
- Array.set(array, (int)pos, e);
+ return readJavaArray(array, expectedType, l, in);
}
}
+ private Object readJavaArray(Object array, Schema expectedType, long l,
+ ResolvingDecoder in) throws IOException {
+ Class<?> elementType = array.getClass().getComponentType();
+ if (elementType.isPrimitive()) {
+ return readPrimitiveArray(array, elementType, l, in);
+ } else {
+ return readObjectArray((Object[]) array, expectedType, l, in);
+ }
+ }
+
+ private Object readPrimitiveArray(Object array, Class<?> c, long l, ResolvingDecoder in)
+ throws IOException {
+ return ArrayAccessor.readArray(array, c, l, in);
+ }
+
+ private Object readObjectArray(Object[] array, Schema expectedType, long l,
+ ResolvingDecoder in) throws IOException {
+ int index = 0;
+ do {
+ int limit = index + (int) l;
+ while (index < limit) {
+ Object element = read(null, expectedType, in);
+ array[index] = element;
+ index++;
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return array;
+ }
+
+ private Object readCollection(Collection<Object> c, Schema expectedType,
+ long l, ResolvingDecoder in) throws IOException {
+ do {
+ for (int i = 0; i < l; i++) {
+ Object element = read(null, expectedType, in);
+ c.add(element);
+ }
+ } while ((l = in.arrayNext()) > 0);
+ return c;
+ }
+
@Override
protected Object readString(Object old, Decoder in) throws IOException {
return super.readString(null, in).toString();
@@ -116,7 +175,7 @@ public class ReflectDatumReader<T> exten
protected Object readBytes(Object old, Schema s, Decoder in)
throws IOException {
ByteBuffer bytes = in.readBytes(null);
- Class c = ReflectData.getClassProp(s, ReflectData.CLASS_PROP);
+ Class<?> c = ReflectData.getClassProp(s, SpecificData.CLASS_PROP);
if (c != null && c.isArray()) {
byte[] result = new byte[bytes.remaining()];
bytes.get(result);
@@ -130,12 +189,27 @@ public class ReflectDatumReader<T> exten
protected Object readInt(Object old,
Schema expected, Decoder in) throws IOException {
Object value = in.readInt();
- String intClass = expected.getProp(ReflectData.CLASS_PROP);
+ String intClass = expected.getProp(SpecificData.CLASS_PROP);
if (Byte.class.getName().equals(intClass))
value = ((Integer)value).byteValue();
else if (Short.class.getName().equals(intClass))
value = ((Integer)value).shortValue();
+ else if (Character.class.getName().equals(intClass))
+ value = ((Character)(char)(int)(Integer)value);
return value;
}
+ @Override
+ protected void readField(Object record, Field f, Object oldDatum,
+ ResolvingDecoder in, Object state) throws IOException {
+ if (state != null) {
+ FieldAccessor accessor = ((FieldAccessor[]) state)[f.pos()];
+ if (accessor != null && !Schema.Type.UNION.equals(f.schema().getType())
+ && accessor.supportsIO()) {
+ accessor.read(record, in);
+ return;
+ }
+ }
+ super.readField(record, f, oldDatum, in, state);
+ }
}
Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectDatumWriter.java Tue Apr 30 16:18:26 2013
@@ -17,14 +17,14 @@
*/
package org.apache.avro.reflect;
-import java.lang.reflect.Array;
import java.io.IOException;
-import java.util.Iterator;
import java.util.Collection;
+import org.apache.avro.AvroRuntimeException;
import org.apache.avro.Schema;
-import org.apache.avro.specific.SpecificDatumWriter;
+import org.apache.avro.Schema.Field;
import org.apache.avro.io.Encoder;
+import org.apache.avro.specific.SpecificDatumWriter;
/**
* {@link org.apache.avro.io.DatumWriter DatumWriter} for existing classes
@@ -54,29 +54,74 @@ public class ReflectDatumWriter<T> exten
protected ReflectDatumWriter(ReflectData reflectData) {
super(reflectData);
}
-
- @Override
- @SuppressWarnings("unchecked")
- protected long getArraySize(Object array) {
- if (array instanceof Collection)
- return ((Collection)array).size();
- return Array.getLength(array);
- }
+ /** Called to write a array. May be overridden for alternate array
+ * representations.*/
@Override
- @SuppressWarnings("unchecked")
- protected Iterator<Object> getArrayElements(final Object array) {
- if (array instanceof Collection)
- return ((Collection<Object>)array).iterator();
- return new Iterator<Object>() {
- private int i = 0;
- private final int length = Array.getLength(array);
- public boolean hasNext() { return i < length; }
- public Object next() { return Array.get(array, i++); }
- public void remove() { throw new UnsupportedOperationException(); }
- };
+ protected void writeArray(Schema schema, Object datum, Encoder out)
+ throws IOException {
+ if (datum instanceof Collection) {
+ super.writeArray(schema, datum, out);
+ return;
+ }
+ Class<?> elementClass = datum.getClass().getComponentType();
+ if (null == elementClass) {
+ // not a Collection or an Array
+ throw new AvroRuntimeException("Array data must be a Collection or Array");
+ }
+ Schema element = schema.getElementType();
+ if (elementClass.isPrimitive()) {
+ Schema.Type type = element.getType();
+ out.writeArrayStart();
+ switch(type) {
+ case BOOLEAN:
+ if(elementClass.isPrimitive())
+ ArrayAccessor.writeArray((boolean[]) datum, out);
+ break;
+ case DOUBLE:
+ ArrayAccessor.writeArray((double[]) datum, out);
+ break;
+ case FLOAT:
+ ArrayAccessor.writeArray((float[]) datum, out);
+ break;
+ case INT:
+ if(elementClass.equals(int.class)) {
+ ArrayAccessor.writeArray((int[]) datum, out);
+ } else if(elementClass.equals(char.class)) {
+ ArrayAccessor.writeArray((char[]) datum, out);
+ } else if(elementClass.equals(short.class)) {
+ ArrayAccessor.writeArray((short[]) datum, out);
+ } else {
+ arrayError(elementClass, type);
+ }
+ break;
+ case LONG:
+ ArrayAccessor.writeArray((long[]) datum, out);
+ break;
+ default:
+ arrayError(elementClass, type);
+ }
+ out.writeArrayEnd();
+ } else {
+ out.writeArrayStart();
+ writeObjectArray(element, (Object[]) datum, out);
+ out.writeArrayEnd();
+ }
}
-
+
+ private void writeObjectArray(Schema element, Object[] data, Encoder out) throws IOException {
+ int size = data.length;
+ out.setItemCount(size);
+ for (int i = 0; i < size; i++) {
+ this.write(element, data[i], out);
+ }
+ }
+
+ private void arrayError(Class<?> cl, Schema.Type type) {
+ throw new AvroRuntimeException("Error writing array with inner type " +
+ cl + " and avro type: " + type);
+ }
+
@Override
protected void writeBytes(Object datum, Encoder out) throws IOException {
if (datum instanceof byte[])
@@ -92,6 +137,8 @@ public class ReflectDatumWriter<T> exten
datum = ((Byte)datum).intValue();
else if (datum instanceof Short)
datum = ((Short)datum).intValue();
+ else if (datum instanceof Character)
+ datum = (int)(char)(Character)datum;
try {
super.write(schema, datum, out);
} catch (NullPointerException e) { // improve error message
@@ -102,4 +149,17 @@ public class ReflectDatumWriter<T> exten
}
}
+ @Override
+ protected void writeField(Object record, Field f, Encoder out, Object state)
+ throws IOException {
+ if (state != null) {
+ FieldAccessor accessor = ((FieldAccessor[]) state)[f.pos()];
+ if (accessor != null && !Schema.Type.UNION.equals(f.schema().getType())
+ && accessor.supportsIO()) {
+ accessor.write(record, out);
+ return;
+ }
+ }
+ super.writeField(record, f, out, state);
+ }
}
Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectionUtil.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import org.apache.avro.AvroRuntimeException;
+
+/**
+ * A few utility methods for using @link{java.misc.Unsafe}, mostly for private
+ * use.
+ *
+ * Use of Unsafe on Android is forbidden, as Android provides only a very
+ * limited functionality for this class compared to the JDK version.
+ *
+ */
+
+class ReflectionUtil {
+
+ private ReflectionUtil() {
+ }
+
+ private static final FieldAccess FIELD_ACCESS;
+ static {
+ // load only one implementation of FieldAccess
+ // so it is monomorphic and the JIT can inline
+ FieldAccess access = null;
+ try {
+ FieldAccess unsafeAccess = load(
+ "org.apache.avro.reflect.FieldAccessUnsafe", FieldAccess.class);
+ if (validate(unsafeAccess)) {
+ access = unsafeAccess;
+ }
+ } catch (Throwable ignored) {
+ }
+ if (access == null) {
+ try {
+ FieldAccess reflectAccess = load(
+ "org.apache.avro.reflect.FieldAccessReflect", FieldAccess.class);
+ if (validate(reflectAccess)) {
+ access = reflectAccess;
+ }
+ } catch (Throwable oops) {
+ throw new AvroRuntimeException(
+ "Unable to load a functional FieldAccess class!");
+ }
+ }
+ FIELD_ACCESS = access;
+ }
+
+ private static <T> T load(String name, Class<T> type) throws Exception {
+ return ReflectionUtil.class.getClassLoader().loadClass(name)
+ .asSubclass(type).newInstance();
+ }
+
+ public static FieldAccess getFieldAccess() {
+ return FIELD_ACCESS;
+ }
+
+ private static boolean validate(FieldAccess access) throws Exception {
+ return new AccessorTestClass().validate(access);
+ }
+
+ private static final class AccessorTestClass {
+ boolean b = true;
+ byte by = 0xf;
+ char c = 'c';
+ short s = 123;
+ int i = 999;
+ long l = 12345L;
+ float f = 2.2f;
+ double d = 4.4d;
+ Object o = "foo";
+ Integer i2 = 555;
+
+ private boolean validate(FieldAccess access) throws Exception {
+ boolean valid = true;
+ valid &= validField(access, "b", b, false);
+ valid &= validField(access, "by", by, (byte) 0xaf);
+ valid &= validField(access, "c", c, 'C');
+ valid &= validField(access, "s", s, (short) 321);
+ valid &= validField(access, "i", i, 111);
+ valid &= validField(access, "l", l, 54321L);
+ valid &= validField(access, "f", f, 0.2f);
+ valid &= validField(access, "d", d, 0.4d);
+ valid &= validField(access, "o", o, new Object());
+ valid &= validField(access, "i2", i2, -555);
+ return valid;
+ }
+
+ private boolean validField(FieldAccess access, String name,
+ Object original, Object toSet) throws Exception {
+ FieldAccessor a;
+ boolean valid = true;
+ a = accessor(access, name);
+ valid &= original.equals(a.get(this));
+ a.set(this, toSet);
+ valid &= !original.equals(a.get(this));
+ return valid;
+ }
+
+ private FieldAccessor accessor(FieldAccess access, String name)
+ throws Exception {
+ return access.getAccessor(this.getClass().getDeclaredField(name));
+ }
+ }
+
+}
Modified: avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java?rev=1477712&r1=1477711&r2=1477712&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java (original)
+++ avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java Tue Apr 30 16:18:26 2013
@@ -24,29 +24,29 @@ import static org.junit.Assert.assertTru
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.nio.ByteBuffer;
+import java.lang.reflect.Array;
import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import org.apache.avro.AvroRuntimeException;
import org.apache.avro.AvroTypeException;
import org.apache.avro.Protocol;
import org.apache.avro.Schema;
-import org.codehaus.jackson.node.NullNode;
-
import org.apache.avro.Schema.Field;
-import org.apache.avro.reflect.TestReflect.SampleRecord.AnotherSampleRecord;
-import org.apache.avro.io.DecoderFactory;
+import org.apache.avro.generic.GenericData;
import org.apache.avro.io.Decoder;
+import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.Encoder;
import org.apache.avro.io.EncoderFactory;
-import org.apache.avro.generic.GenericData;
-
+import org.apache.avro.reflect.TestReflect.SampleRecord.AnotherSampleRecord;
+import org.codehaus.jackson.node.NullNode;
import org.junit.Test;
public class TestReflect {
@@ -104,21 +104,21 @@ public class TestReflect {
}
@Test public void testUnionWithCollection() {
- Schema s = Schema.parse
+ Schema s = new Schema.Parser().parse
("[\"null\", {\"type\":\"array\",\"items\":\"float\"}]");
GenericData data = ReflectData.get();
assertEquals(1, data.resolveUnion(s, new ArrayList<Float>()));
}
@Test public void testUnionWithMap() {
- Schema s = Schema.parse
+ Schema s = new Schema.Parser().parse
("[\"null\", {\"type\":\"map\",\"values\":\"float\"}]");
GenericData data = ReflectData.get();
assertEquals(1, data.resolveUnion(s, new HashMap<String,Float>()));
}
@Test public void testUnionWithBytes() {
- Schema s = Schema.parse ("[\"null\", \"bytes\"]");
+ Schema s = new Schema.Parser().parse ("[\"null\", \"bytes\"]");
GenericData data = ReflectData.get();
assertEquals(1, data.resolveUnion(s, ByteBuffer.wrap(new byte[]{1})));
}
@@ -134,6 +134,7 @@ public class TestReflect {
listField.add("foo");
}
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R1)) return false;
R1 that = (R1)o;
@@ -157,7 +158,7 @@ public class TestReflect {
"{\"type\":\"array\",\"items\":\"string\""
+",\"java-class\":\"java.util.List\"}");
}
-
+
@Test public void testR1() throws Exception {
checkReadWrite(new R1());
}
@@ -167,6 +168,7 @@ public class TestReflect {
private String[] arrayField;
private Collection<String> collectionField;
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R2)) return false;
R2 that = (R2)o;
@@ -187,6 +189,7 @@ public class TestReflect {
public static class R3 {
private int[] intArray;
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R3)) return false;
R3 that = (R3)o;
@@ -206,6 +209,7 @@ public class TestReflect {
public short[] shorts;
public byte b;
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R4)) return false;
R4 that = (R4)o;
@@ -231,6 +235,7 @@ public class TestReflect {
public static class R7 extends R6 {
public int value;
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R7)) return false;
return this.value == ((R7)o).value;
@@ -238,6 +243,7 @@ public class TestReflect {
}
public static class R8 extends R6 {
public float value;
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R8)) return false;
return this.value == ((R8)o).value;
@@ -247,6 +253,7 @@ public class TestReflect {
// test arrays of union annotated class
public static class R9 {
public R6[] r6s;
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R9)) return false;
return Arrays.equals(this.r6s, ((R9)o).r6s);
@@ -296,7 +303,9 @@ public class TestReflect {
@Stringable public static class R10 {
private String text;
public R10(String text) { this.text = text; }
+ @Override
public String toString() { return text; }
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R10)) return false;
return this.text.equals(((R10)o).text);
@@ -313,6 +322,7 @@ public class TestReflect {
// test Nullable annotation on field
public static class R11 {
@Nullable private String text;
+ @Override
public boolean equals(Object o) {
if (!(o instanceof R11)) return false;
R11 that = (R11)o;
@@ -364,6 +374,7 @@ public class TestReflect {
}
// test error
+ @SuppressWarnings("serial")
public static class E1 extends Exception {}
public static interface P2 {
void error() throws E1;
@@ -390,9 +401,9 @@ public class TestReflect {
}
@Test public void testNoPackage() throws Exception {
- Class noPackage = Class.forName("NoPackage");
+ Class<?> noPackage = Class.forName("NoPackage");
Schema s = ReflectData.get().getSchema(noPackage);
- assertEquals(noPackage.getName(), ReflectData.get().getClassName(s));
+ assertEquals(noPackage.getName(), ReflectData.getClassName(s));
}
void checkReadWrite(Object object) throws Exception {
@@ -477,10 +488,12 @@ public class TestReflect {
public int x = 1;
private int y = 2;
+ @Override
public int hashCode() {
return x + y;
}
+ @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
@@ -508,12 +521,14 @@ public class TestReflect {
this.s = new SampleRecord();
}
+ @Override
public int hashCode() {
int hash = (a != null ? a.hashCode() : 0);
hash += (s != null ? s.hashCode() : 0);
return hash;
}
+ @Override
public boolean equals(Object other) {
if (other instanceof AnotherSampleRecord) {
AnotherSampleRecord o = (AnotherSampleRecord) other;
@@ -592,12 +607,65 @@ public class TestReflect {
// test that this instance can be written & re-read
checkBinary(schema, record);
}
+
+ @Test
+ public void testPrimitiveArray() throws Exception {
+ testPrimitiveArrays(false);
+ }
+
+ @Test
+ public void testPrimitiveArrayBlocking() throws Exception {
+ testPrimitiveArrays(true);
+ }
+
+ private void testPrimitiveArrays(boolean blocking) throws Exception {
+ testPrimitiveArray(boolean.class, blocking);
+ testPrimitiveArray(byte.class, blocking);
+ testPrimitiveArray(short.class, blocking);
+ testPrimitiveArray(char.class, blocking);
+ testPrimitiveArray(int.class, blocking);
+ testPrimitiveArray(long.class, blocking);
+ testPrimitiveArray(float.class, blocking);
+ testPrimitiveArray(double.class, blocking);
+ }
+
+ private void testPrimitiveArray(Class<?> c, boolean blocking) throws Exception {
+ ReflectData data = new ReflectData();
+ Random r = new Random();
+ int size = 200;
+ Object array = Array.newInstance(c, size);
+ Schema s = data.getSchema(array.getClass());
+ for(int i = 0; i < size; i++) {
+ Array.set(array, i, randomFor(c, r));
+ }
+ checkBinary(data, s, array, false, blocking);
+ }
+
+ private Object randomFor(Class<?> c, Random r) {
+ if (c == boolean.class)
+ return r.nextBoolean();
+ if (c == int.class)
+ return r.nextInt();
+ if (c == long.class)
+ return r.nextLong();
+ if (c == byte.class)
+ return (byte)r.nextInt();
+ if (c == float.class)
+ return r.nextFloat();
+ if (c == double.class)
+ return r.nextDouble();
+ if (c == char.class)
+ return (char)r.nextInt();
+ if (c == short.class)
+ return (short)r.nextInt();
+ return null;
+ }
/** Test union of null and an array. */
@Test
public void testNullArray() throws Exception {
String json = "[{\"type\":\"array\", \"items\": \"long\"}, \"null\"]";
- Schema schema = Schema.parse(json);
+ Schema schema = new Schema.Parser().parse(json);
checkBinary(schema, null);
}
@@ -610,8 +678,9 @@ public class TestReflect {
checkStringable(java.io.File.class, "foo.bar");
}
+ @SuppressWarnings({ "unchecked", "rawtypes" })
public void checkStringable(Class c, String value) throws Exception {
- ReflectData data = new ReflectData().get();
+ ReflectData data = new ReflectData();
Schema schema = data.getSchema(c);
assertEquals
("{\"type\":\"string\",\"java-class\":\""+c.getName()+"\"}",
@@ -659,17 +728,26 @@ public class TestReflect {
}
public static void checkBinary(ReflectData reflectData, Schema schema,
- Object datum, boolean equals)
- throws IOException {
+ Object datum, boolean equals) throws IOException {
+ checkBinary(reflectData, schema, datum, equals, false);
+ }
+
+ private static void checkBinary(ReflectData reflectData, Schema schema,
+ Object datum, boolean equals, boolean blocking) throws IOException {
ReflectDatumWriter<Object> writer = new ReflectDatumWriter<Object>(schema);
ByteArrayOutputStream out = new ByteArrayOutputStream();
+ if (!blocking) {
+ writer.write(datum, EncoderFactory.get().directBinaryEncoder(out, null));
+ } else {
+ writer.write(datum, new EncoderFactory().configureBlockSize(64)
+ .blockingBinaryEncoder(out, null));
+ }
writer.write(datum, EncoderFactory.get().directBinaryEncoder(out, null));
byte[] data = out.toByteArray();
ReflectDatumReader<Object> reader = new ReflectDatumReader<Object>(schema);
- Object decoded =
- reader.read(null, DecoderFactory.get().binaryDecoder(
- data, null));
+ Object decoded = reader.read(null,
+ DecoderFactory.get().binaryDecoder(data, null));
assertEquals(0, reflectData.compare(datum, decoded, schema, equals));
}
@@ -690,5 +768,4 @@ public class TestReflect {
}
}
-
}
Added: avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java?rev=1477712&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java (added)
+++ avro/trunk/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectionUtil.java Tue Apr 30 16:18:26 2013
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.avro.reflect;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Test;
+
+public class TestReflectionUtil {
+
+ @Test
+ public void testUnsafeUtil() {
+ new Tester().checkUnsafe();
+ }
+
+ @Test
+ public void testUnsafeWhenNotExists() throws Exception {
+ ClassLoader cl = new NoUnsafe();
+ Class<?> testerClass = cl.loadClass(Tester.class.getName());
+ testerClass.getDeclaredMethod("checkUnsafe").invoke(testerClass.newInstance());
+ }
+
+ public static final class Tester {
+ public Tester() {}
+ public void checkUnsafe() {
+ ReflectionUtil.getFieldAccess();
+ }
+
+ }
+
+ private static final class NoUnsafe extends ClassLoader {
+ private ClassLoader parent = TestReflectionUtil.class.getClassLoader();
+
+ @Override
+ public java.lang.Class<?> loadClass(String name)
+ throws ClassNotFoundException {
+ Class<?> clazz = findLoadedClass(name);
+ if (clazz != null) {
+ return clazz;
+ }
+ if ("sun.misc.Unsafe".equals(name)) {
+ throw new ClassNotFoundException(name);
+ }
+ if (!name.startsWith("org.apache.avro.")) {
+ return parent.loadClass(name);
+ }
+
+ InputStream data = parent.getResourceAsStream(name.replace('.', '/')
+ + ".class");
+ byte[] buf = new byte[10240]; // big enough, too lazy to loop
+ int size;
+ try {
+ size = data.read(buf);
+ } catch (IOException e) {
+ throw new ClassNotFoundException();
+ }
+ clazz = defineClass(name, buf, 0, size);
+ resolveClass(clazz);
+ return clazz;
+ }
+
+ }
+}