You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by re...@apache.org on 2020/02/24 23:09:53 UTC
[beam] branch master updated: Support null fields in rows with
ByteBuddy generated code.
This is an automated email from the ASF dual-hosted git repository.
reuvenlax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new b63bd59 Support null fields in rows with ByteBuddy generated code.
new 16fa12c Merge pull request #10926 from reuvenlax/fix_bytebuddy_nullable
b63bd59 is described below
commit b63bd5928864f37d88746f48566e6ed56b8b6660
Author: Reuven Lax <re...@relax-macbookpro3.roam.corp.google.com>
AuthorDate: Thu Feb 20 21:46:34 2020 -0800
Support null fields in rows with ByteBuddy generated code.
---
.../beam/sdk/schemas/utils/AutoValueUtils.java | 3 +
.../beam/sdk/schemas/utils/AvroByteBuddyUtils.java | 3 +
.../beam/sdk/schemas/utils/ByteBuddyUtils.java | 290 +++++++++++++--------
.../beam/sdk/schemas/utils/ConvertHelpers.java | 3 +
.../beam/sdk/schemas/utils/JavaBeanUtils.java | 6 +
.../apache/beam/sdk/schemas/utils/POJOUtils.java | 9 +
.../beam/sdk/schemas/JavaBeanSchemaTest.java | 44 ++++
.../beam/sdk/schemas/JavaFieldSchemaTest.java | 54 ++++
.../beam/sdk/schemas/utils/TestJavaBeans.java | 199 ++++++++++++++
.../apache/beam/sdk/schemas/utils/TestPOJOs.java | 105 ++++++++
.../extensions/protobuf/ProtoByteBuddyUtils.java | 3 +
11 files changed, 612 insertions(+), 107 deletions(-)
diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AutoValueUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AutoValueUtils.java
index 775fe65..0435cb8 100644
--- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AutoValueUtils.java
+++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AutoValueUtils.java
@@ -44,6 +44,7 @@ import org.apache.beam.sdk.schemas.utils.ByteBuddyUtils.TypeConversionsFactory;
import org.apache.beam.sdk.util.common.ReflectHelpers;
import org.apache.beam.sdk.values.TypeDescriptor;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.ByteBuddy;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.asm.AsmVisitorWrapper;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.method.MethodDescription.ForLoadedMethod;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.type.TypeDescription.ForLoadedType;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.DynamicType;
@@ -62,6 +63,7 @@ import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.byte
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodReturn;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.ClassWriter;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.matcher.ElementMatchers;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.collect.Lists;
@@ -207,6 +209,7 @@ public class AutoValueUtils {
.intercept(
new BuilderCreateInstruction(types, setterMethods, builderClass, buildMethod));
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(ReflectHelpers.findClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded()
diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AvroByteBuddyUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AvroByteBuddyUtils.java
index 3bb7e8d..8957cf0 100644
--- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AvroByteBuddyUtils.java
+++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/AvroByteBuddyUtils.java
@@ -34,6 +34,7 @@ import org.apache.beam.sdk.schemas.utils.ReflectUtils.ClassWithSchema;
import org.apache.beam.sdk.util.common.ReflectHelpers;
import org.apache.beam.sdk.values.TypeDescriptor;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.ByteBuddy;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.asm.AsmVisitorWrapper;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.type.TypeDescription.ForLoadedType;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.DynamicType;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
@@ -43,6 +44,7 @@ import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.byte
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.collection.ArrayAccess;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.constant.IntegerConstant;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.ClassWriter;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.matcher.ElementMatchers;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.collect.Maps;
@@ -89,6 +91,7 @@ class AvroByteBuddyUtils {
.intercept(construct);
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(clazz.getClassLoader()),
diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java
index f1850e9..8a5ddbe 100644
--- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java
+++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ByteBuddyUtils.java
@@ -44,6 +44,7 @@ import org.apache.beam.sdk.values.TypeParameter;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.ByteBuddy;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.NamingStrategy;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.NamingStrategy.SuffixingRandom.BaseNameResolver;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.asm.AsmVisitorWrapper;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.method.MethodDescription.ForLoadedConstructor;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.method.MethodDescription.ForLoadedMethod;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.type.TypeDescription;
@@ -52,6 +53,7 @@ import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.DynamicType
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.scaffold.InstrumentedType;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.Implementation;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.Implementation.Context;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.ByteCodeAppender.Size;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.Duplication;
@@ -68,6 +70,10 @@ import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.byte
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodReturn;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.ClassWriter;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.Label;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.MethodVisitor;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.Opcodes;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.matcher.ElementMatchers;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.utility.RandomString;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.base.Function;
@@ -143,6 +149,42 @@ public class ByteBuddyUtils {
}
};
+ // This StackManipulation returns onNotNull if the result of readValue is not null. Otherwise it
+ // returns null.
+ static class ShortCircuitReturnNull implements StackManipulation {
+ private final StackManipulation readValue;
+ private final StackManipulation onNotNull;
+
+ ShortCircuitReturnNull(StackManipulation readValue, StackManipulation onNotNull) {
+ this.readValue = readValue;
+ this.onNotNull = onNotNull;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public Size apply(MethodVisitor methodVisitor, Context context) {
+ Size size = new Size(0, 0);
+ size = size.aggregate(readValue.apply(methodVisitor, context));
+ Label label = new Label();
+ Label skipLabel = new Label();
+ methodVisitor.visitJumpInsn(Opcodes.IFNONNULL, label);
+ size = size.aggregate(new Size(-1, 0));
+ methodVisitor.visitInsn(Opcodes.ACONST_NULL);
+ methodVisitor.visitJumpInsn(Opcodes.GOTO, skipLabel);
+ size = size.aggregate(new Size(0, 1));
+ methodVisitor.visitLabel(label);
+ // We set COMPUTE_FRAMES on our builders, which causes ASM to calculate the correct frame
+ // information to insert here.
+ size = size.aggregate(onNotNull.apply(methodVisitor, context));
+ methodVisitor.visitLabel(skipLabel);
+ return size;
+ }
+ }
+
// Create a new FieldValueGetter subclass.
@SuppressWarnings("unchecked")
public static DynamicType.Builder<FieldValueGetter> subclassGetterInterface(
@@ -401,6 +443,7 @@ public class ByteBuddyUtils {
});
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(((Class) fromType).getClassLoader()),
@@ -584,6 +627,7 @@ public class ByteBuddyUtils {
.getOnly()));
// Generate a SerializableFunction to convert the element-type objects.
+ StackManipulation stackManipulation;
final TypeDescriptor finalComponentType = ReflectUtils.boxIfPrimitive(componentType);
if (!finalComponentType.hasUnresolvedParameters()) {
Type convertedComponentType =
@@ -594,16 +638,18 @@ public class ByteBuddyUtils {
componentType.getRawType(),
convertedComponentType,
(s) -> getFactory().createGetterConversions(s).convert(finalComponentType)));
- return createTransformingContainer(functionType, readListValue);
+ stackManipulation = createTransformingContainer(functionType, readListValue);
} else {
- return readListValue;
+ stackManipulation = readListValue;
}
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
protected StackManipulation convertIterable(TypeDescriptor<?> type) {
TypeDescriptor componentType = ReflectUtils.getIterableComponentType(type);
Type convertedComponentType = getFactory().createTypeConversion(true).convert(componentType);
+
final TypeDescriptor finalComponentType = ReflectUtils.boxIfPrimitive(componentType);
if (!finalComponentType.hasUnresolvedParameters()) {
ForLoadedType functionType =
@@ -612,7 +658,8 @@ public class ByteBuddyUtils {
componentType.getRawType(),
convertedComponentType,
(s) -> getFactory().createGetterConversions(s).convert(finalComponentType)));
- return createTransformingContainer(functionType, readValue);
+ StackManipulation stackManipulation = createTransformingContainer(functionType, readValue);
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
} else {
return readValue;
}
@@ -630,7 +677,8 @@ public class ByteBuddyUtils {
componentType.getRawType(),
convertedComponentType,
(s) -> getFactory().createGetterConversions(s).convert(finalComponentType)));
- return createTransformingContainer(functionType, readValue);
+ StackManipulation stackManipulation = createTransformingContainer(functionType, readValue);
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
} else {
return readValue;
}
@@ -648,7 +696,8 @@ public class ByteBuddyUtils {
componentType.getRawType(),
convertedComponentType,
(s) -> getFactory().createGetterConversions(s).convert(finalComponentType)));
- return createTransformingContainer(functionType, readValue);
+ StackManipulation stackManipulation = createTransformingContainer(functionType, readValue);
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
} else {
return readValue;
}
@@ -675,27 +724,31 @@ public class ByteBuddyUtils {
valueType.getRawType(),
convertedValueType,
(s) -> getFactory().createGetterConversions(s).convert(valueType)));
- return new Compound(
- readValue,
- TypeCreation.of(keyFunctionType),
- Duplication.SINGLE,
- MethodInvocation.invoke(
- keyFunctionType
- .getDeclaredMethods()
- .filter(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(0)))
- .getOnly()),
- TypeCreation.of(valueFunctionType),
- Duplication.SINGLE,
- MethodInvocation.invoke(
- valueFunctionType
- .getDeclaredMethods()
- .filter(ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(0)))
- .getOnly()),
- MethodInvocation.invoke(
- BYTE_BUDDY_UTILS_TYPE
- .getDeclaredMethods()
- .filter(ElementMatchers.named("getTransformingMap"))
- .getOnly()));
+ StackManipulation stackManipulation =
+ new Compound(
+ readValue,
+ TypeCreation.of(keyFunctionType),
+ Duplication.SINGLE,
+ MethodInvocation.invoke(
+ keyFunctionType
+ .getDeclaredMethods()
+ .filter(
+ ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(0)))
+ .getOnly()),
+ TypeCreation.of(valueFunctionType),
+ Duplication.SINGLE,
+ MethodInvocation.invoke(
+ valueFunctionType
+ .getDeclaredMethods()
+ .filter(
+ ElementMatchers.isConstructor().and(ElementMatchers.takesArguments(0)))
+ .getOnly()),
+ MethodInvocation.invoke(
+ BYTE_BUDDY_UTILS_TYPE
+ .getDeclaredMethods()
+ .filter(ElementMatchers.named("getTransformingMap"))
+ .getOnly()));
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
} else {
return readValue;
}
@@ -773,7 +826,8 @@ public class ByteBuddyUtils {
.and(ElementMatchers.takesArguments(ForLoadedType.of(long.class))))
.getOnly()));
- return new StackManipulation.Compound(stackManipulations);
+ StackManipulation stackManipulation = new StackManipulation.Compound(stackManipulations);
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
@@ -784,14 +838,17 @@ public class ByteBuddyUtils {
// We must extract the array from the ByteBuffer before returning.
// NOTE: we only support array-backed byte buffers in these POJOs. Others (e.g. mmaped
// files) are not supported.
- return new Compound(
- readValue,
- MethodInvocation.invoke(
- BYTE_BUFFER_TYPE
- .getDeclaredMethods()
- .filter(
- ElementMatchers.named("array").and(ElementMatchers.returns(BYTE_ARRAY_TYPE)))
- .getOnly()));
+ StackManipulation stackManipulation =
+ new Compound(
+ readValue,
+ MethodInvocation.invoke(
+ BYTE_BUFFER_TYPE
+ .getDeclaredMethods()
+ .filter(
+ ElementMatchers.named("array")
+ .and(ElementMatchers.returns(BYTE_ARRAY_TYPE)))
+ .getOnly()));
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
@@ -803,13 +860,16 @@ public class ByteBuddyUtils {
// Otherwise, generate the following code:
// return value.toString();
- return new Compound(
- readValue,
- MethodInvocation.invoke(
- CHAR_SEQUENCE_TYPE
- .getDeclaredMethods()
- .filter(ElementMatchers.named("toString").and(ElementMatchers.takesArguments(0)))
- .getOnly()));
+ StackManipulation stackManipulation =
+ new Compound(
+ readValue,
+ MethodInvocation.invoke(
+ CHAR_SEQUENCE_TYPE
+ .getDeclaredMethods()
+ .filter(
+ ElementMatchers.named("toString").and(ElementMatchers.takesArguments(0)))
+ .getOnly()));
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
@@ -824,17 +884,20 @@ public class ByteBuddyUtils {
@Override
protected StackManipulation convertEnum(TypeDescriptor<?> type) {
- return new Compound(
- readValue,
- MethodInvocation.invoke(
- ENUM_TYPE
- .getDeclaredMethods()
- .filter(ElementMatchers.named("ordinal").and(ElementMatchers.takesArguments(0)))
- .getOnly()),
- Assigner.DEFAULT.assign(
- INTEGER_TYPE.asUnboxed().asGenericType(),
- INTEGER_TYPE.asGenericType(),
- Typing.STATIC));
+ StackManipulation stackManipulation =
+ new Compound(
+ readValue,
+ MethodInvocation.invoke(
+ ENUM_TYPE
+ .getDeclaredMethods()
+ .filter(
+ ElementMatchers.named("ordinal").and(ElementMatchers.takesArguments(0)))
+ .getOnly()),
+ Assigner.DEFAULT.assign(
+ INTEGER_TYPE.asUnboxed().asGenericType(),
+ INTEGER_TYPE.asGenericType(),
+ Typing.STATIC));
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
@@ -877,6 +940,7 @@ public class ByteBuddyUtils {
Type rowElementType =
getFactory().createTypeConversion(false).convert(type.getComponentType());
final TypeDescriptor arrayElementType = ReflectUtils.boxIfPrimitive(type.getComponentType());
+ StackManipulation readTransformedValue = readValue;
if (!arrayElementType.hasUnresolvedParameters()) {
ForLoadedType conversionFunction =
new ForLoadedType(
@@ -884,13 +948,13 @@ public class ByteBuddyUtils {
TypeDescriptor.of(rowElementType).getRawType(),
Primitives.wrap(arrayElementType.getRawType()),
(s) -> getFactory().createSetterConversions(s).convert(arrayElementType)));
- readValue = createTransformingContainer(conversionFunction, readValue);
+ readTransformedValue = createTransformingContainer(conversionFunction, readValue);
}
// Extract an array from the collection.
StackManipulation stackManipulation =
new Compound(
- readValue,
+ readTransformedValue,
TypeCasting.to(COLLECTION_TYPE),
// Call Collection.toArray(T[[]) to extract the array. Push new T[0] on the stack
// before
@@ -920,7 +984,7 @@ public class ByteBuddyUtils {
.and(ElementMatchers.takesArguments(arrayType)))
.getOnly()));
}
- return stackManipulation;
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
@@ -939,7 +1003,7 @@ public class ByteBuddyUtils {
(s) -> getFactory().createSetterConversions(s).convert(iterableElementType)));
StackManipulation transformedContainer =
createTransformingContainer(conversionFunction, readValue);
- return transformedContainer;
+ return new ShortCircuitReturnNull(readValue, transformedContainer);
} else {
return readValue;
}
@@ -962,7 +1026,7 @@ public class ByteBuddyUtils {
(s) -> getFactory().createSetterConversions(s).convert(collectionElementType)));
StackManipulation transformedContainer =
createTransformingContainer(conversionFunction, readValue);
- return transformedContainer;
+ return new ShortCircuitReturnNull(readValue, transformedContainer);
} else {
return readValue;
}
@@ -976,6 +1040,7 @@ public class ByteBuddyUtils {
.convert(ReflectUtils.getIterableComponentType(type));
final TypeDescriptor collectionElementType = ReflectUtils.getIterableComponentType(type);
+ StackManipulation readTrasformedValue = readValue;
if (!collectionElementType.hasUnresolvedParameters()) {
ForLoadedType conversionFunction =
new ForLoadedType(
@@ -983,12 +1048,12 @@ public class ByteBuddyUtils {
TypeDescriptor.of(rowElementType).getRawType(),
collectionElementType.getRawType(),
(s) -> getFactory().createSetterConversions(s).convert(collectionElementType)));
- readValue = createTransformingContainer(conversionFunction, readValue);
+ readTrasformedValue = createTransformingContainer(conversionFunction, readValue);
}
// TODO: Don't copy if already a list!
StackManipulation transformedList =
new Compound(
- readValue,
+ readTrasformedValue,
MethodInvocation.invoke(
new ForLoadedType(Lists.class)
.getDeclaredMethods()
@@ -996,7 +1061,7 @@ public class ByteBuddyUtils {
ElementMatchers.named("newArrayList")
.and(ElementMatchers.takesArguments(Iterable.class)))
.getOnly()));
- return transformedList;
+ return new ShortCircuitReturnNull(readValue, transformedList);
}
@Override
@@ -1008,6 +1073,7 @@ public class ByteBuddyUtils {
getFactory().createTypeConversion(false).convert(ReflectUtils.getMapType(type, 1));
final TypeDescriptor valueElementType = ReflectUtils.getMapType(type, 1);
+ StackManipulation readTrasformedValue = readValue;
if (!keyElementType.hasUnresolvedParameters()
&& !valueElementType.hasUnresolvedParameters()) {
ForLoadedType keyConversionFunction =
@@ -1022,7 +1088,7 @@ public class ByteBuddyUtils {
TypeDescriptor.of(rowValueType).getRawType(),
valueElementType.getRawType(),
(s) -> getFactory().createSetterConversions(s).convert(valueElementType)));
- readValue =
+ readTrasformedValue =
new Compound(
readValue,
TypeCreation.of(keyConversionFunction),
@@ -1047,7 +1113,7 @@ public class ByteBuddyUtils {
.filter(ElementMatchers.named("getTransformingMap"))
.getOnly()));
}
- return readValue;
+ return new ShortCircuitReturnNull(readValue, readTrasformedValue);
}
@Override
@@ -1077,6 +1143,7 @@ public class ByteBuddyUtils {
.getDeclaredMethods()
.filter(ElementMatchers.named("getMillis"))
.getOnly()));
+
if (type.isSubtypeOf(TypeDescriptor.of(BaseLocal.class))) {
// Access DateTimeZone.UTC
stackManipulations.add(
@@ -1112,7 +1179,8 @@ public class ByteBuddyUtils {
.getOnly()));
}
- return new Compound(stackManipulations);
+ StackManipulation stackManipulation = new Compound(stackManipulations);
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
@@ -1121,17 +1189,19 @@ public class ByteBuddyUtils {
// return ByteBuffer.wrap((byte[]) value);
// We currently assume that a byte[] setter will always accept a parameter of type byte[].
- return new Compound(
- readValue,
- TypeCasting.to(BYTE_ARRAY_TYPE),
- // Create a new ByteBuffer that wraps this byte[].
- MethodInvocation.invoke(
- BYTE_BUFFER_TYPE
- .getDeclaredMethods()
- .filter(
- ElementMatchers.named("wrap")
- .and(ElementMatchers.takesArguments(BYTE_ARRAY_TYPE)))
- .getOnly()));
+ StackManipulation stackManipulation =
+ new Compound(
+ readValue,
+ TypeCasting.to(BYTE_ARRAY_TYPE),
+ // Create a new ByteBuffer that wraps this byte[].
+ MethodInvocation.invoke(
+ BYTE_BUFFER_TYPE
+ .getDeclaredMethods()
+ .filter(
+ ElementMatchers.named("wrap")
+ .and(ElementMatchers.takesArguments(BYTE_ARRAY_TYPE)))
+ .getOnly()));
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
@@ -1145,20 +1215,22 @@ public class ByteBuddyUtils {
// return new T((CharacterSequence) value).
ForLoadedType loadedType = new ForLoadedType(type.getRawType());
- return new StackManipulation.Compound(
- TypeCreation.of(loadedType),
- Duplication.SINGLE,
- // Load the parameter and cast it to a CharSequence.
- readValue,
- TypeCasting.to(CHAR_SEQUENCE_TYPE),
- // Create an element of the field type that wraps this one.
- MethodInvocation.invoke(
- loadedType
- .getDeclaredMethods()
- .filter(
- ElementMatchers.isConstructor()
- .and(ElementMatchers.takesArguments(CHAR_SEQUENCE_TYPE)))
- .getOnly()));
+ StackManipulation stackManipulation =
+ new StackManipulation.Compound(
+ TypeCreation.of(loadedType),
+ Duplication.SINGLE,
+ // Load the parameter and cast it to a CharSequence.
+ readValue,
+ TypeCasting.to(CHAR_SEQUENCE_TYPE),
+ // Create an element of the field type that wraps this one.
+ MethodInvocation.invoke(
+ loadedType
+ .getDeclaredMethods()
+ .filter(
+ ElementMatchers.isConstructor()
+ .and(ElementMatchers.takesArguments(CHAR_SEQUENCE_TYPE)))
+ .getOnly()));
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
@@ -1178,24 +1250,28 @@ public class ByteBuddyUtils {
ForLoadedType loadedType = new ForLoadedType(type.getRawType());
// Convert the stored ordinal back to the Java enum constant.
- return new Compound(
- // Call EnumType::values() to get an array of all enum constants.
- MethodInvocation.invoke(
- loadedType
- .getDeclaredMethods()
- .filter(
- ElementMatchers.named("values")
- .and(ElementMatchers.isStatic().and(ElementMatchers.takesArguments(0))))
- .getOnly()),
- // Read the integer enum value.
- readValue,
- // Unbox Integer -> int before accessing the array.
- Assigner.DEFAULT.assign(
- INTEGER_TYPE.asBoxed().asGenericType(),
- INTEGER_TYPE.asUnboxed().asGenericType(),
- Typing.STATIC),
- // Access the array to return the Java enum type.
- ArrayAccess.REFERENCE.load());
+ StackManipulation stackManipulation =
+ new Compound(
+ // Call EnumType::values() to get an array of all enum constants.
+ MethodInvocation.invoke(
+ loadedType
+ .getDeclaredMethods()
+ .filter(
+ ElementMatchers.named("values")
+ .and(
+ ElementMatchers.isStatic()
+ .and(ElementMatchers.takesArguments(0))))
+ .getOnly()),
+ // Read the integer enum value.
+ readValue,
+ // Unbox Integer -> int before accessing the array.
+ Assigner.DEFAULT.assign(
+ INTEGER_TYPE.asBoxed().asGenericType(),
+ INTEGER_TYPE.asUnboxed().asGenericType(),
+ Typing.STATIC),
+ // Access the array to return the Java enum type.
+ ArrayAccess.REFERENCE.load());
+ return new ShortCircuitReturnNull(readValue, stackManipulation);
}
@Override
diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ConvertHelpers.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ConvertHelpers.java
index 5c435b5..1e786d1 100644
--- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ConvertHelpers.java
+++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/ConvertHelpers.java
@@ -35,6 +35,7 @@ import org.apache.beam.sdk.util.common.ReflectHelpers;
import org.apache.beam.sdk.values.Row;
import org.apache.beam.sdk.values.TypeDescriptor;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.ByteBuddy;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.asm.AsmVisitorWrapper;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.type.TypeDescription;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.DynamicType;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
@@ -45,6 +46,7 @@ import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.byte
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.StackManipulation;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodReturn;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.ClassWriter;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.matcher.ElementMatchers;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.primitives.Primitives;
@@ -160,6 +162,7 @@ public class ConvertHelpers {
(DynamicType.Builder<SerializableFunction>) new ByteBuddy().subclass(genericType);
try {
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.method(ElementMatchers.named("apply"))
.intercept(new ConvertPrimitiveInstruction(outputType, typeConversionsFactory))
.make()
diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java
index e25342b..0b4fe79 100644
--- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java
+++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/JavaBeanUtils.java
@@ -38,6 +38,7 @@ import org.apache.beam.sdk.schemas.utils.ByteBuddyUtils.TypeConversionsFactory;
import org.apache.beam.sdk.schemas.utils.ReflectUtils.ClassWithSchema;
import org.apache.beam.sdk.util.common.ReflectHelpers;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.ByteBuddy;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.asm.AsmVisitorWrapper;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.method.MethodDescription.ForLoadedMethod;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.DynamicType;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
@@ -51,6 +52,7 @@ import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.byte
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodReturn;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.ClassWriter;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.matcher.ElementMatchers;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.collect.Maps;
@@ -140,6 +142,7 @@ public class JavaBeanUtils {
builder = implementGetterMethods(builder, typeInformation, typeConversionsFactory);
try {
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(
@@ -203,6 +206,7 @@ public class JavaBeanUtils {
builder = implementSetterMethods(builder, typeInformation, typeConversionsFactory);
try {
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(
@@ -267,6 +271,7 @@ public class JavaBeanUtils {
new ConstructorCreateInstruction(
types, clazz, constructor, typeConversionsFactory));
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(clazz.getClassLoader()),
@@ -314,6 +319,7 @@ public class JavaBeanUtils {
types, clazz, creator, typeConversionsFactory));
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(clazz.getClassLoader()),
diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java
index aa968b4..7b69e09 100644
--- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java
+++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/utils/POJOUtils.java
@@ -42,6 +42,7 @@ import org.apache.beam.sdk.schemas.utils.ReflectUtils.ClassWithSchema;
import org.apache.beam.sdk.util.common.ReflectHelpers;
import org.apache.beam.sdk.values.TypeDescriptor;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.ByteBuddy;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.asm.AsmVisitorWrapper;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.field.FieldDescription.ForLoadedField;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.description.type.TypeDescription.ForLoadedType;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.dynamic.DynamicType;
@@ -61,6 +62,7 @@ import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.byte
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodReturn;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
+import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.jar.asm.ClassWriter;
import org.apache.beam.vendor.bytebuddy.v1_9_3.net.bytebuddy.matcher.ElementMatchers;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.collect.Maps;
@@ -146,6 +148,7 @@ public class POJOUtils {
.intercept(new SetFieldCreateInstruction(fields, clazz, typeConversionsFactory));
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(clazz.getClassLoader()),
@@ -194,6 +197,7 @@ public class POJOUtils {
types, clazz, constructor, typeConversionsFactory));
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(clazz.getClassLoader()),
@@ -241,6 +245,7 @@ public class POJOUtils {
types, clazz, creator, typeConversionsFactory));
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(ReflectHelpers.findClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded()
@@ -284,6 +289,7 @@ public class POJOUtils {
implementGetterMethods(builder, field, typeInformation.getName(), typeConversionsFactory);
try {
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(field.getDeclaringClass().getClassLoader()),
@@ -305,6 +311,7 @@ public class POJOUtils {
String name,
TypeConversionsFactory typeConversionsFactory) {
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.method(ElementMatchers.named("name"))
.intercept(FixedValue.reference(name))
.method(ElementMatchers.named("get"))
@@ -362,6 +369,7 @@ public class POJOUtils {
builder = implementSetterMethods(builder, field, typeConversionsFactory);
try {
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(
ReflectHelpers.findClassLoader(field.getDeclaringClass().getClassLoader()),
@@ -382,6 +390,7 @@ public class POJOUtils {
Field field,
TypeConversionsFactory typeConversionsFactory) {
return builder
+ .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES))
.method(ElementMatchers.named("name"))
.intercept(FixedValue.reference(field.getName()))
.method(ElementMatchers.named("set"))
diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/JavaBeanSchemaTest.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/JavaBeanSchemaTest.java
index feb51db..5ec69e5 100644
--- a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/JavaBeanSchemaTest.java
+++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/JavaBeanSchemaTest.java
@@ -17,6 +17,7 @@
*/
package org.apache.beam.sdk.schemas;
+import static org.apache.beam.sdk.schemas.utils.TestJavaBeans.ALL_NULLABLE_BEAN_SCHEMA;
import static org.apache.beam.sdk.schemas.utils.TestJavaBeans.ARRAY_OF_BYTE_ARRAY_BEAM_SCHEMA;
import static org.apache.beam.sdk.schemas.utils.TestJavaBeans.ITERABLE_BEAM_SCHEMA;
import static org.apache.beam.sdk.schemas.utils.TestJavaBeans.NESTED_ARRAYS_BEAM_SCHEMA;
@@ -27,6 +28,7 @@ import static org.apache.beam.sdk.schemas.utils.TestJavaBeans.PRIMITIVE_ARRAY_BE
import static org.apache.beam.sdk.schemas.utils.TestJavaBeans.SIMPLE_BEAN_SCHEMA;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -37,6 +39,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.beam.sdk.schemas.utils.SchemaTestUtils;
+import org.apache.beam.sdk.schemas.utils.TestJavaBeans.AllNullableBean;
import org.apache.beam.sdk.schemas.utils.TestJavaBeans.ArrayOfByteArray;
import org.apache.beam.sdk.schemas.utils.TestJavaBeans.IterableBean;
import org.apache.beam.sdk.schemas.utils.TestJavaBeans.MismatchingNullableBean;
@@ -160,6 +163,47 @@ public class JavaBeanSchemaTest {
}
@Test
+ public void testNullableToRow() throws NoSuchSchemaException {
+ SchemaRegistry registry = SchemaRegistry.createDefault();
+ AllNullableBean bean = new AllNullableBean();
+ Row row = registry.getToRowFunction(AllNullableBean.class).apply(bean);
+
+ assertEquals(12, row.getFieldCount());
+ assertNull(row.getString("str"));
+ assertNull(row.getByte("aByte"));
+ assertNull(row.getInt16("aShort"));
+ assertNull(row.getInt32("anInt"));
+ assertNull(row.getInt64("aLong"));
+ assertNull(row.getBoolean("aBoolean"));
+ assertNull(row.getDateTime("dateTime"));
+ assertNull(row.getDateTime("instant"));
+ assertNull(row.getBytes("bytes"));
+ assertNull(row.getBytes("byteBuffer"));
+ assertNull(row.getDecimal("bigDecimal"));
+ assertNull(row.getString("stringBuilder"));
+ }
+
+ @Test
+ public void testNullableFromRow() throws NoSuchSchemaException {
+ SchemaRegistry registry = SchemaRegistry.createDefault();
+ Row row = Row.nullRow(ALL_NULLABLE_BEAN_SCHEMA);
+
+ AllNullableBean bean = registry.getFromRowFunction(AllNullableBean.class).apply(row);
+ assertNull(bean.getStr());
+ assertNull(bean.getaByte());
+ assertNull(bean.getaShort());
+ assertNull(bean.getAnInt());
+ assertNull(bean.getaLong());
+ assertNull(bean.isaBoolean());
+ assertNull(bean.getDateTime());
+ assertNull(bean.getInstant());
+ assertNull(bean.getBytes());
+ assertNull(bean.getByteBuffer());
+ assertNull(bean.getBigDecimal());
+ assertNull(bean.getStringBuilder());
+ }
+
+ @Test
public void testToRowSerializable() throws NoSuchSchemaException {
SchemaRegistry registry = SchemaRegistry.createDefault();
SerializableUtils.ensureSerializableRoundTrip(registry.getToRowFunction(SimpleBean.class));
diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/JavaFieldSchemaTest.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/JavaFieldSchemaTest.java
index 4134a57..9dcdcd4 100644
--- a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/JavaFieldSchemaTest.java
+++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/JavaFieldSchemaTest.java
@@ -23,6 +23,7 @@ import static org.apache.beam.sdk.schemas.utils.TestPOJOs.NESTED_MAP_POJO_SCHEMA
import static org.apache.beam.sdk.schemas.utils.TestPOJOs.NESTED_NULLABLE_SCHEMA;
import static org.apache.beam.sdk.schemas.utils.TestPOJOs.NESTED_POJO_SCHEMA;
import static org.apache.beam.sdk.schemas.utils.TestPOJOs.NULLABLES_SCHEMA;
+import static org.apache.beam.sdk.schemas.utils.TestPOJOs.NULLABLE_POJO_SCHEMA;
import static org.apache.beam.sdk.schemas.utils.TestPOJOs.POJO_WITH_ENUM_SCHEMA;
import static org.apache.beam.sdk.schemas.utils.TestPOJOs.POJO_WITH_ITERABLE;
import static org.apache.beam.sdk.schemas.utils.TestPOJOs.POJO_WITH_NESTED_ARRAY_SCHEMA;
@@ -47,6 +48,7 @@ import org.apache.beam.sdk.schemas.utils.TestPOJOs.NestedArrayPOJO;
import org.apache.beam.sdk.schemas.utils.TestPOJOs.NestedArraysPOJO;
import org.apache.beam.sdk.schemas.utils.TestPOJOs.NestedMapPOJO;
import org.apache.beam.sdk.schemas.utils.TestPOJOs.NestedPOJO;
+import org.apache.beam.sdk.schemas.utils.TestPOJOs.NullablePOJO;
import org.apache.beam.sdk.schemas.utils.TestPOJOs.POJOWithNestedNullable;
import org.apache.beam.sdk.schemas.utils.TestPOJOs.POJOWithNullables;
import org.apache.beam.sdk.schemas.utils.TestPOJOs.PojoWithEnum;
@@ -91,6 +93,10 @@ public class JavaFieldSchemaTest {
new StringBuilder(name).append("builder"));
}
+ private NullablePOJO createNullable() {
+ return new NullablePOJO(null, null, null, null, null, null, null, null, null, null, null, null);
+ }
+
private AnnotatedSimplePojo createAnnotated(String name) {
return new AnnotatedSimplePojo(
name,
@@ -190,6 +196,54 @@ public class JavaFieldSchemaTest {
}
@Test
+ public void testNullableSchema() throws NoSuchSchemaException {
+ SchemaRegistry registry = SchemaRegistry.createDefault();
+ Schema schema = registry.getSchema(NullablePOJO.class);
+ SchemaTestUtils.assertSchemaEquivalent(NULLABLE_POJO_SCHEMA, schema);
+ }
+
+ @Test
+ public void testNullableToRow() throws NoSuchSchemaException {
+ SchemaRegistry registry = SchemaRegistry.createDefault();
+ NullablePOJO pojo = createNullable();
+ Row row = registry.getToRowFunction(NullablePOJO.class).apply(pojo);
+
+ assertEquals(12, row.getFieldCount());
+ assertNull(row.getString("str"));
+ assertNull(row.getByte("aByte"));
+ assertNull(row.getInt16("aShort"));
+ assertNull(row.getInt32("anInt"));
+ assertNull(row.getInt64("aLong"));
+ assertNull(row.getBoolean("aBoolean"));
+ assertNull(row.getDateTime("dateTime"));
+ assertNull(row.getDateTime("instant"));
+ assertNull(row.getBytes("bytes"));
+ assertNull(row.getBytes("byteBuffer"));
+ assertNull(row.getDecimal("bigDecimal"));
+ assertNull(row.getString("stringBuilder"));
+ }
+
+ @Test
+ public void testNullableFromRow() throws NoSuchSchemaException {
+ SchemaRegistry registry = SchemaRegistry.createDefault();
+ Row row = Row.nullRow(NULLABLE_POJO_SCHEMA);
+
+ NullablePOJO pojo = registry.getFromRowFunction(NullablePOJO.class).apply(row);
+ assertNull(pojo.str);
+ assertNull(pojo.aByte);
+ assertNull(pojo.aShort);
+ assertNull(pojo.anInt);
+ assertNull(pojo.aLong);
+ assertNull(pojo.aBoolean);
+ assertNull(pojo.dateTime);
+ assertNull(pojo.instant);
+ assertNull(pojo.bytes);
+ assertNull(pojo.byteBuffer);
+ assertNull(pojo.bigDecimal);
+ assertNull(pojo.stringBuilder);
+ }
+
+ @Test
public void testToRowSerializable() throws NoSuchSchemaException {
SchemaRegistry registry = SchemaRegistry.createDefault();
SerializableUtils.ensureSerializableRoundTrip(registry.getToRowFunction(SimplePOJO.class));
diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/utils/TestJavaBeans.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/utils/TestJavaBeans.java
index 32cf264..6783945 100644
--- a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/utils/TestJavaBeans.java
+++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/utils/TestJavaBeans.java
@@ -313,6 +313,205 @@ public class TestJavaBeans {
.addStringField("stringBuilder")
.build();
+ /** A simple Bean containing basic nullable types. * */
+ @DefaultSchema(JavaBeanSchema.class)
+ public static class AllNullableBean {
+ @Nullable private String str;
+ @Nullable private Byte aByte;
+ @Nullable private Short aShort;
+ @Nullable private Integer anInt;
+ @Nullable private Long aLong;
+ @Nullable private Boolean aBoolean;
+ @Nullable private DateTime dateTime;
+ @Nullable private Instant instant;
+ @Nullable private byte[] bytes;
+ @Nullable private ByteBuffer byteBuffer;
+ @Nullable private BigDecimal bigDecimal;
+ @Nullable private StringBuilder stringBuilder;
+
+ public AllNullableBean() {
+ this.str = null;
+ this.aByte = null;
+ this.aShort = null;
+ this.anInt = null;
+ this.aLong = null;
+ this.aBoolean = null;
+ this.dateTime = null;
+ this.instant = null;
+ this.bytes = null;
+ this.byteBuffer = null;
+ this.bigDecimal = null;
+ this.stringBuilder = null;
+ }
+
+ @Nullable
+ public String getStr() {
+ return str;
+ }
+
+ public void setStr(@Nullable String str) {
+ this.str = str;
+ }
+
+ @Nullable
+ public Byte getaByte() {
+ return aByte;
+ }
+
+ public void setaByte(@Nullable Byte aByte) {
+ this.aByte = aByte;
+ }
+
+ @Nullable
+ public Short getaShort() {
+ return aShort;
+ }
+
+ public void setaShort(@Nullable Short aShort) {
+ this.aShort = aShort;
+ }
+
+ @Nullable
+ public Integer getAnInt() {
+ return anInt;
+ }
+
+ public void setAnInt(@Nullable Integer anInt) {
+ this.anInt = anInt;
+ }
+
+ @Nullable
+ public Long getaLong() {
+ return aLong;
+ }
+
+ public void setaLong(@Nullable Long aLong) {
+ this.aLong = aLong;
+ }
+
+ @Nullable
+ public Boolean isaBoolean() {
+ return aBoolean;
+ }
+
+ public void setaBoolean(@Nullable Boolean aBoolean) {
+ this.aBoolean = aBoolean;
+ }
+
+ @Nullable
+ public DateTime getDateTime() {
+ return dateTime;
+ }
+
+ public void setDateTime(@Nullable DateTime dateTime) {
+ this.dateTime = dateTime;
+ }
+
+ @Nullable
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ public void setBytes(@Nullable byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ @Nullable
+ public ByteBuffer getByteBuffer() {
+ return byteBuffer;
+ }
+
+ public void setByteBuffer(@Nullable ByteBuffer byteBuffer) {
+ this.byteBuffer = byteBuffer;
+ }
+
+ @Nullable
+ public Instant getInstant() {
+ return instant;
+ }
+
+ public void setInstant(@Nullable Instant instant) {
+ this.instant = instant;
+ }
+
+ @Nullable
+ public BigDecimal getBigDecimal() {
+ return bigDecimal;
+ }
+
+ public void setBigDecimal(@Nullable BigDecimal bigDecimal) {
+ this.bigDecimal = bigDecimal;
+ }
+
+ @Nullable
+ public StringBuilder getStringBuilder() {
+ return stringBuilder;
+ }
+
+ public void setStringBuilder(@Nullable StringBuilder stringBuilder) {
+ this.stringBuilder = stringBuilder;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SimpleBean that = (SimpleBean) o;
+ return Objects.equals(aByte, that.aByte)
+ && Objects.equals(aShort, that.aShort)
+ && Objects.equals(anInt, that.anInt)
+ && Objects.equals(aLong, that.aLong)
+ && Objects.equals(aBoolean, that.aBoolean)
+ && Objects.equals(str, that.str)
+ && Objects.equals(dateTime, that.dateTime)
+ && Objects.equals(instant, that.instant)
+ && Arrays.equals(bytes, that.bytes)
+ && Objects.equals(byteBuffer, that.byteBuffer)
+ && Objects.equals(bigDecimal, that.bigDecimal)
+ && Objects.equals(stringBuilder, that.stringBuilder);
+ }
+
+ @Override
+ public int hashCode() {
+ int result =
+ Objects.hash(
+ str,
+ aByte,
+ aShort,
+ anInt,
+ aLong,
+ aBoolean,
+ dateTime,
+ instant,
+ byteBuffer,
+ bigDecimal,
+ stringBuilder);
+ result = 31 * result + Arrays.hashCode(bytes);
+ return result;
+ }
+ }
+
+ /** The schema for {@link AllNullableBean}. * */
+ public static final Schema ALL_NULLABLE_BEAN_SCHEMA =
+ Schema.builder()
+ .addNullableField("str", FieldType.STRING)
+ .addNullableField("aByte", FieldType.BYTE)
+ .addNullableField("aShort", FieldType.INT16)
+ .addNullableField("anInt", FieldType.INT32)
+ .addNullableField("aLong", FieldType.INT64)
+ .addNullableField("aBoolean", FieldType.BOOLEAN)
+ .addNullableField("dateTime", FieldType.DATETIME)
+ .addNullableField("instant", FieldType.DATETIME)
+ .addNullableField("bytes", FieldType.BYTES)
+ .addNullableField("byteBuffer", FieldType.BYTES)
+ .addNullableField("bigDecimal", FieldType.DECIMAL)
+ .addNullableField("stringBuilder", FieldType.STRING)
+ .build();
+
/** A simple Bean containing basic types. * */
@DefaultSchema(JavaBeanSchema.class)
public static class SimpleBeanWithAnnotations {
diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/utils/TestPOJOs.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/utils/TestPOJOs.java
index 0952d17..be779c0 100644
--- a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/utils/TestPOJOs.java
+++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/utils/TestPOJOs.java
@@ -901,4 +901,109 @@ public class TestPOJOs {
Schema.builder()
.addLogicalTypeField("color", EnumerationType.create("RED", "GREEN", "BLUE"))
.build();
+
+ /** A simple POJO containing nullable basic types. * */
+ @DefaultSchema(JavaFieldSchema.class)
+ public static class NullablePOJO {
+ @Nullable public String str;
+ @Nullable public Byte aByte;
+ @Nullable public Short aShort;
+ @Nullable public Integer anInt;
+ @Nullable public Long aLong;
+ @Nullable public Boolean aBoolean;
+ @Nullable public DateTime dateTime;
+ @Nullable public Instant instant;
+ @Nullable public byte[] bytes;
+ @Nullable public ByteBuffer byteBuffer;
+ @Nullable public BigDecimal bigDecimal;
+ @Nullable public StringBuilder stringBuilder;
+
+ public NullablePOJO() {}
+
+ public NullablePOJO(
+ String str,
+ Byte aByte,
+ Short aShort,
+ Integer anInt,
+ Long aLong,
+ Boolean aBoolean,
+ DateTime dateTime,
+ Instant instant,
+ byte[] bytes,
+ ByteBuffer byteBuffer,
+ BigDecimal bigDecimal,
+ StringBuilder stringBuilder) {
+ this.str = str;
+ this.aByte = aByte;
+ this.aShort = aShort;
+ this.anInt = anInt;
+ this.aLong = aLong;
+ this.aBoolean = aBoolean;
+ this.dateTime = dateTime;
+ this.instant = instant;
+ this.bytes = bytes;
+ this.byteBuffer = byteBuffer;
+ this.bigDecimal = bigDecimal;
+ this.stringBuilder = stringBuilder;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ NullablePOJO that = (NullablePOJO) o;
+ return Objects.equals(aByte, that.aByte)
+ && Objects.equals(aShort, that.aShort)
+ && Objects.equals(anInt, that.anInt)
+ && Objects.equals(aLong, that.aLong)
+ && Objects.equals(aBoolean, that.aBoolean)
+ && Objects.equals(str, that.str)
+ && Objects.equals(dateTime, that.dateTime)
+ && Objects.equals(instant, that.instant)
+ && Arrays.equals(bytes, that.bytes)
+ && Objects.equals(byteBuffer, that.byteBuffer)
+ && Objects.equals(bigDecimal, that.bigDecimal)
+ && Objects.equals(stringBuilder.toString(), that.stringBuilder.toString());
+ }
+
+ @Override
+ public int hashCode() {
+ int result =
+ Objects.hash(
+ str,
+ aByte,
+ aShort,
+ anInt,
+ aLong,
+ aBoolean,
+ dateTime,
+ instant,
+ byteBuffer,
+ bigDecimal,
+ stringBuilder);
+ result = 31 * result + Arrays.hashCode(bytes);
+ return result;
+ }
+ }
+
+ /** The schema for {@link NullablePOJO}. * */
+ public static final Schema NULLABLE_POJO_SCHEMA =
+ Schema.builder()
+ .addNullableField("str", FieldType.STRING)
+ .addNullableField("aByte", FieldType.BYTE)
+ .addNullableField("aShort", FieldType.INT16)
+ .addNullableField("anInt", FieldType.INT32)
+ .addNullableField("aLong", FieldType.INT64)
+ .addNullableField("aBoolean", FieldType.BOOLEAN)
+ .addNullableField("dateTime", FieldType.DATETIME)
+ .addNullableField("instant", FieldType.DATETIME)
+ .addNullableField("bytes", FieldType.BYTES)
+ .addNullableField("byteBuffer", FieldType.BYTES)
+ .addNullableField("bigDecimal", FieldType.DECIMAL)
+ .addNullableField("stringBuilder", FieldType.STRING)
+ .build();
}
diff --git a/sdks/java/extensions/protobuf/src/main/java/org/apache/beam/sdk/extensions/protobuf/ProtoByteBuddyUtils.java b/sdks/java/extensions/protobuf/src/main/java/org/apache/beam/sdk/extensions/protobuf/ProtoByteBuddyUtils.java
index bafa38f..2b0af35 100644
--- a/sdks/java/extensions/protobuf/src/main/java/org/apache/beam/sdk/extensions/protobuf/ProtoByteBuddyUtils.java
+++ b/sdks/java/extensions/protobuf/src/main/java/org/apache/beam/sdk/extensions/protobuf/ProtoByteBuddyUtils.java
@@ -1019,6 +1019,9 @@ public class ProtoByteBuddyUtils {
.intercept(new BuilderSupplier(protoClass));
Supplier supplier =
builder
+ .visit(
+ new AsmVisitorWrapper.ForDeclaredMethods()
+ .writerFlags(ClassWriter.COMPUTE_FRAMES))
.make()
.load(ReflectHelpers.findClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded()