From b78063fe7618ffaa0c8ceea69d2c11a2ed0d2bcd Mon Sep 17 00:00:00 2001 From: dmitry-fa Date: Fri, 28 Aug 2020 12:11:25 +0300 Subject: [PATCH 1/7] fix: Filters should be serializable --- .../bigtable/data/v2/models/Filters.java | 45 ++++++++++++---- .../bigtable/data/v2/models/FiltersTest.java | 52 +++++++++++++++++++ .../com/google/bigtable/v2/RowFilter.java | 40 +++++++++----- .../bigtable/v2/RowFilterOrBuilder.java | 9 ++-- 4 files changed, 120 insertions(+), 26 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java index bd310cd2c..42d5a7af4 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java @@ -200,6 +200,7 @@ public Filter label(@Nonnull String label) { // Implementations of target specific filters. /** DSL for adding filters to a chain. */ public static final class ChainFilter implements Filter { + private static final long serialVersionUID = -89237431180618430L; private RowFilter.Chain.Builder builder; private ChainFilter() { @@ -241,6 +242,7 @@ public ChainFilter clone() { /** DSL for adding filters to the interleave list. */ public static final class InterleaveFilter implements Filter { + private static final long serialVersionUID = -3866166430679348474L; private RowFilter.Interleave.Builder builder; private InterleaveFilter() { @@ -281,6 +283,7 @@ public InterleaveFilter clone() { /** DSL for configuring a conditional filter. */ public static final class ConditionFilter implements Filter { + private static final long serialVersionUID = -7576541105323990049L; private RowFilter.Condition.Builder builder; private ConditionFilter(@Nonnull Filter predicate) { @@ -323,7 +326,9 @@ public ConditionFilter clone() { } } - public static final class KeyFilter { + public static final class KeyFilter implements Serializable { + private static final long serialVersionUID = 5137765114285539458L; + private KeyFilter() {} /** @@ -383,7 +388,9 @@ public Filter sample(double probability) { } } - public static final class FamilyFilter { + public static final class FamilyFilter implements Serializable { + private static final long serialVersionUID = -4470936841191831553L; + private FamilyFilter() {} /** @@ -405,7 +412,9 @@ public Filter exactMatch(@Nonnull String value) { } } - public static final class QualifierFilter { + public static final class QualifierFilter implements Serializable { + private static final long serialVersionUID = -1274850022909506559L; + private QualifierFilter() {} /** @@ -459,7 +468,8 @@ public QualifierRangeFilter rangeWithinFamily(@Nonnull String family) { /** Matches only cells from columns within the given range. */ public static final class QualifierRangeFilter - extends AbstractByteStringRange implements Filter, Serializable { + extends AbstractByteStringRange implements Filter { + private static final long serialVersionUID = -1909319911147913630L; private final String family; private QualifierRangeFilter(String family) { @@ -505,7 +515,9 @@ public QualifierRangeFilter clone() { } } - public static final class TimestampFilter { + public static final class TimestampFilter implements Serializable { + private static final long serialVersionUID = 5284219722591464991L; + private TimestampFilter() {} /** @@ -529,7 +541,9 @@ public TimestampRangeFilter exact(Long exactTimestamp) { /** Matches only cells with microsecond timestamps within the given range. */ public static final class TimestampRangeFilter - extends AbstractTimestampRange implements Filter, Serializable { + extends AbstractTimestampRange implements Filter { + private static final long serialVersionUID = 8410980338603335276L; + private TimestampRangeFilter() {} @InternalApi @@ -571,7 +585,9 @@ public TimestampRangeFilter clone() { } } - public static final class ValueFilter { + public static final class ValueFilter implements Serializable { + private static final long serialVersionUID = 6722715229238811179L; + private ValueFilter() {} /** @@ -628,7 +644,9 @@ public Filter strip() { /** Matches only cells with values that fall within the given value range. */ public static final class ValueRangeFilter extends AbstractByteStringRange - implements Filter, Serializable { + implements Filter { + private static final long serialVersionUID = -2452360677825047088L; + private ValueRangeFilter() {} @InternalApi @@ -668,7 +686,9 @@ public ValueRangeFilter clone() { } } - public static final class OffsetFilter { + public static final class OffsetFilter implements Serializable { + private static final long serialVersionUID = 3228791236971884041L; + private OffsetFilter() {} /** @@ -681,7 +701,9 @@ public Filter cellsPerRow(int count) { } } - public static final class LimitFilter { + public static final class LimitFilter implements Serializable { + private static final long serialVersionUID = -794915549003008940L; + private LimitFilter() {} /** @@ -705,6 +727,7 @@ public Filter cellsPerColumn(int count) { } private static final class SimpleFilter implements Filter { + private static final long serialVersionUID = 3595911451325189833L; private final RowFilter proto; private SimpleFilter(@Nonnull RowFilter proto) { @@ -729,7 +752,7 @@ public SimpleFilter clone() { } @InternalExtensionOnly - public interface Filter extends Cloneable { + public interface Filter extends Cloneable, Serializable { @InternalApi RowFilter toProto(); } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java index ad7902525..e9c30f4a5 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java @@ -17,6 +17,7 @@ import static com.google.cloud.bigtable.data.v2.models.Filters.FILTERS; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import com.google.bigtable.v2.ColumnRange; import com.google.bigtable.v2.RowFilter; @@ -26,6 +27,17 @@ import com.google.bigtable.v2.TimestampRange; import com.google.bigtable.v2.ValueRange; import com.google.protobuf.ByteString; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -528,4 +540,44 @@ public void labelTest() { assertThat(actualFilter).isEqualTo(expectedFilter); } + + @Test + public void serializationTest() { + // checks that the all objects returned by the all methods of the Filters class + // can be serialized/deserialized. + + // map methodName -> methodArguments (for those methods which require parameters) + Map parameterMap = new HashMap<>(); + parameterMap.put("condition", new Object[] {FILTERS.pass()}); + parameterMap.put("label", new Object[] {"label"}); + parameterMap.put("fromProto", new Object[] {FILTERS.label("label").toProto()}); + + for (Method m : Filters.class.getDeclaredMethods()) { + String name = m.getName(); + if (Modifier.isPublic(m.getModifiers())) { + Object[] params = parameterMap.get(name); + if (params == null) { + params = new Object[] {}; + } + try { + trySerialization(m.invoke(FILTERS, params)); + } catch (Exception e) { + fail(name + ": " + e); + } + } + } + } + + private void trySerialization(Object obj) throws IOException, ClassNotFoundException { + Path serFile = Files.createTempFile("filter", ".ser"); + try (ObjectOutputStream outStream = + new ObjectOutputStream(new FileOutputStream(serFile.toFile()))) { + outStream.writeObject(obj); + } + + try (ObjectInputStream inStream = + new ObjectInputStream(new FileInputStream(serFile.toFile()))) { + inStream.readObject(); + } + } } diff --git a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilter.java b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilter.java index 0308b4236..a7c8273d7 100644 --- a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilter.java +++ b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilter.java @@ -18,6 +18,8 @@ package com.google.bigtable.v2; +import java.io.Serializable; + /** * * @@ -56,9 +58,11 @@ */ public final class RowFilter extends com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter) - RowFilterOrBuilder { - private static final long serialVersionUID = 0L; + // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter) + RowFilterOrBuilder, + Serializable { + private static final long serialVersionUID = 5041615103476790190L; + // Use RowFilter.newBuilder() to construct. private RowFilter(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); @@ -380,7 +384,8 @@ public static final class Chain extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter.Chain) ChainOrBuilder { - private static final long serialVersionUID = 0L; + private static final long serialVersionUID = 3029099319991323890L; + // Use Chain.newBuilder() to construct. private Chain(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); @@ -720,8 +725,11 @@ protected Builder newBuilderForType( public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter.Chain) - com.google.bigtable.v2.RowFilter.ChainOrBuilder { + // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter.Chain) + com.google.bigtable.v2.RowFilter.ChainOrBuilder, + Serializable { + private static final long serialVersionUID = 3843969655199755334L; + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.bigtable.v2.DataProto .internal_static_google_bigtable_v2_RowFilter_Chain_descriptor; @@ -1346,8 +1354,9 @@ public com.google.bigtable.v2.RowFilter.Chain getDefaultInstanceForType() { public interface InterleaveOrBuilder extends - // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter.Interleave) - com.google.protobuf.MessageOrBuilder { + // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter.Interleave) + com.google.protobuf.MessageOrBuilder, + Serializable { /** * @@ -1524,7 +1533,7 @@ public static final class Interleave extends com.google.protobuf.GeneratedMessag implements // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter.Interleave) InterleaveOrBuilder { - private static final long serialVersionUID = 0L; + private static final long serialVersionUID = -4010737514587512031L; // Use Interleave.newBuilder() to construct. private Interleave(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); @@ -1968,6 +1977,8 @@ public static final class Builder implements // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter.Interleave) com.google.bigtable.v2.RowFilter.InterleaveOrBuilder { + private static final long serialVersionUID = -194632601621153938L; + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.bigtable.v2.DataProto .internal_static_google_bigtable_v2_RowFilter_Interleave_descriptor; @@ -2952,8 +2963,9 @@ public com.google.bigtable.v2.RowFilter.Interleave getDefaultInstanceForType() { public interface ConditionOrBuilder extends - // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter.Condition) - com.google.protobuf.MessageOrBuilder { + // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter.Condition) + com.google.protobuf.MessageOrBuilder, + Serializable { /** * @@ -3090,7 +3102,7 @@ public static final class Condition extends com.google.protobuf.GeneratedMessage implements // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter.Condition) ConditionOrBuilder { - private static final long serialVersionUID = 0L; + private static final long serialVersionUID = -721556095392940616L; // Use Condition.newBuilder() to construct. private Condition(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); @@ -3574,6 +3586,8 @@ public static final class Builder implements // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter.Condition) com.google.bigtable.v2.RowFilter.ConditionOrBuilder { + private static final long serialVersionUID = 2192459056488847085L; + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.bigtable.v2.DataProto .internal_static_google_bigtable_v2_RowFilter_Condition_descriptor; @@ -5736,6 +5750,8 @@ public static final class Builder extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter) com.google.bigtable.v2.RowFilterOrBuilder { + private static final long serialVersionUID = 3777165723978883886L; + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.bigtable.v2.DataProto .internal_static_google_bigtable_v2_RowFilter_descriptor; diff --git a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilterOrBuilder.java b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilterOrBuilder.java index 3fdcc2956..8eff53128 100644 --- a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilterOrBuilder.java +++ b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilterOrBuilder.java @@ -18,10 +18,13 @@ package com.google.bigtable.v2; +import java.io.Serializable; + public interface RowFilterOrBuilder extends - // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter) - com.google.protobuf.MessageOrBuilder { + // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter) + com.google.protobuf.MessageOrBuilder, + Serializable { /** * @@ -545,5 +548,5 @@ public interface RowFilterOrBuilder */ com.google.protobuf.ByteString getApplyLabelTransformerBytes(); - public com.google.bigtable.v2.RowFilter.FilterCase getFilterCase(); + com.google.bigtable.v2.RowFilter.FilterCase getFilterCase(); } From b3723f2271cd247d681936d01b9cd179b56fe841 Mon Sep 17 00:00:00 2001 From: dmitry-fa Date: Tue, 1 Sep 2020 18:48:35 +0300 Subject: [PATCH 2/7] rollback changes to generated files --- .../com/google/bigtable/v2/RowFilter.java | 40 ++++++------------- .../bigtable/v2/RowFilterOrBuilder.java | 9 ++--- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilter.java b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilter.java index a7c8273d7..0308b4236 100644 --- a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilter.java +++ b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilter.java @@ -18,8 +18,6 @@ package com.google.bigtable.v2; -import java.io.Serializable; - /** * * @@ -58,11 +56,9 @@ */ public final class RowFilter extends com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter) - RowFilterOrBuilder, - Serializable { - private static final long serialVersionUID = 5041615103476790190L; - + // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter) + RowFilterOrBuilder { + private static final long serialVersionUID = 0L; // Use RowFilter.newBuilder() to construct. private RowFilter(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); @@ -384,8 +380,7 @@ public static final class Chain extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter.Chain) ChainOrBuilder { - private static final long serialVersionUID = 3029099319991323890L; - + private static final long serialVersionUID = 0L; // Use Chain.newBuilder() to construct. private Chain(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); @@ -725,11 +720,8 @@ protected Builder newBuilderForType( public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter.Chain) - com.google.bigtable.v2.RowFilter.ChainOrBuilder, - Serializable { - private static final long serialVersionUID = 3843969655199755334L; - + // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter.Chain) + com.google.bigtable.v2.RowFilter.ChainOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.bigtable.v2.DataProto .internal_static_google_bigtable_v2_RowFilter_Chain_descriptor; @@ -1354,9 +1346,8 @@ public com.google.bigtable.v2.RowFilter.Chain getDefaultInstanceForType() { public interface InterleaveOrBuilder extends - // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter.Interleave) - com.google.protobuf.MessageOrBuilder, - Serializable { + // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter.Interleave) + com.google.protobuf.MessageOrBuilder { /** * @@ -1533,7 +1524,7 @@ public static final class Interleave extends com.google.protobuf.GeneratedMessag implements // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter.Interleave) InterleaveOrBuilder { - private static final long serialVersionUID = -4010737514587512031L; + private static final long serialVersionUID = 0L; // Use Interleave.newBuilder() to construct. private Interleave(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); @@ -1977,8 +1968,6 @@ public static final class Builder implements // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter.Interleave) com.google.bigtable.v2.RowFilter.InterleaveOrBuilder { - private static final long serialVersionUID = -194632601621153938L; - public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.bigtable.v2.DataProto .internal_static_google_bigtable_v2_RowFilter_Interleave_descriptor; @@ -2963,9 +2952,8 @@ public com.google.bigtable.v2.RowFilter.Interleave getDefaultInstanceForType() { public interface ConditionOrBuilder extends - // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter.Condition) - com.google.protobuf.MessageOrBuilder, - Serializable { + // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter.Condition) + com.google.protobuf.MessageOrBuilder { /** * @@ -3102,7 +3090,7 @@ public static final class Condition extends com.google.protobuf.GeneratedMessage implements // @@protoc_insertion_point(message_implements:google.bigtable.v2.RowFilter.Condition) ConditionOrBuilder { - private static final long serialVersionUID = -721556095392940616L; + private static final long serialVersionUID = 0L; // Use Condition.newBuilder() to construct. private Condition(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); @@ -3586,8 +3574,6 @@ public static final class Builder implements // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter.Condition) com.google.bigtable.v2.RowFilter.ConditionOrBuilder { - private static final long serialVersionUID = 2192459056488847085L; - public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.bigtable.v2.DataProto .internal_static_google_bigtable_v2_RowFilter_Condition_descriptor; @@ -5750,8 +5736,6 @@ public static final class Builder extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(builder_implements:google.bigtable.v2.RowFilter) com.google.bigtable.v2.RowFilterOrBuilder { - private static final long serialVersionUID = 3777165723978883886L; - public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return com.google.bigtable.v2.DataProto .internal_static_google_bigtable_v2_RowFilter_descriptor; diff --git a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilterOrBuilder.java b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilterOrBuilder.java index 8eff53128..3fdcc2956 100644 --- a/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilterOrBuilder.java +++ b/proto-google-cloud-bigtable-v2/src/main/java/com/google/bigtable/v2/RowFilterOrBuilder.java @@ -18,13 +18,10 @@ package com.google.bigtable.v2; -import java.io.Serializable; - public interface RowFilterOrBuilder extends - // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter) - com.google.protobuf.MessageOrBuilder, - Serializable { + // @@protoc_insertion_point(interface_extends:google.bigtable.v2.RowFilter) + com.google.protobuf.MessageOrBuilder { /** * @@ -548,5 +545,5 @@ public interface RowFilterOrBuilder */ com.google.protobuf.ByteString getApplyLabelTransformerBytes(); - com.google.bigtable.v2.RowFilter.FilterCase getFilterCase(); + public com.google.bigtable.v2.RowFilter.FilterCase getFilterCase(); } From 2208adbd2b09d0cfda88aadfecf8ab531699ee35 Mon Sep 17 00:00:00 2001 From: dmitry-fa Date: Tue, 1 Sep 2020 20:27:05 +0300 Subject: [PATCH 3/7] customize serialization of builders --- .../bigtable/data/v2/models/Filters.java | 29 ++++++++-- .../bigtable/data/v2/models/FiltersTest.java | 56 ++++++++++--------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java index 42d5a7af4..79fc94fa8 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java @@ -25,6 +25,8 @@ import com.google.cloud.bigtable.data.v2.models.Range.AbstractTimestampRange; import com.google.common.base.Preconditions; import com.google.protobuf.ByteString; +import java.io.IOException; +import java.io.ObjectOutputStream; import java.io.Serializable; import javax.annotation.Nonnull; @@ -200,8 +202,13 @@ public Filter label(@Nonnull String label) { // Implementations of target specific filters. /** DSL for adding filters to a chain. */ public static final class ChainFilter implements Filter { - private static final long serialVersionUID = -89237431180618430L; - private RowFilter.Chain.Builder builder; + private static final long serialVersionUID = -6756759448656768478L; + private transient RowFilter.Chain.Builder builder; + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeObject(builder.build()); + } private ChainFilter() { this.builder = RowFilter.Chain.newBuilder(); @@ -242,8 +249,13 @@ public ChainFilter clone() { /** DSL for adding filters to the interleave list. */ public static final class InterleaveFilter implements Filter { - private static final long serialVersionUID = -3866166430679348474L; - private RowFilter.Interleave.Builder builder; + private static final long serialVersionUID = -6356151037337889421L; + private transient RowFilter.Interleave.Builder builder; + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeObject(builder.build()); + } private InterleaveFilter() { builder = RowFilter.Interleave.newBuilder(); @@ -283,8 +295,13 @@ public InterleaveFilter clone() { /** DSL for configuring a conditional filter. */ public static final class ConditionFilter implements Filter { - private static final long serialVersionUID = -7576541105323990049L; - private RowFilter.Condition.Builder builder; + private static final long serialVersionUID = -2720899822014446776L; + private transient RowFilter.Condition.Builder builder; + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeObject(builder.build()); + } private ConditionFilter(@Nonnull Filter predicate) { Preconditions.checkNotNull(predicate); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java index e9c30f4a5..d1da9bdce 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java @@ -27,17 +27,14 @@ import com.google.bigtable.v2.TimestampRange; import com.google.bigtable.v2.ValueRange; import com.google.protobuf.ByteString; -import java.io.FileInputStream; -import java.io.FileOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -542,41 +539,46 @@ public void labelTest() { } @Test - public void serializationTest() { + public void serializationTest() throws InvocationTargetException, IllegalAccessException { // checks that the all objects returned by the all methods of the Filters class // can be serialized/deserialized. - // map methodName -> methodArguments (for those methods which require parameters) - Map parameterMap = new HashMap<>(); - parameterMap.put("condition", new Object[] {FILTERS.pass()}); - parameterMap.put("label", new Object[] {"label"}); - parameterMap.put("fromProto", new Object[] {FILTERS.label("label").toProto()}); - for (Method m : Filters.class.getDeclaredMethods()) { String name = m.getName(); if (Modifier.isPublic(m.getModifiers())) { - Object[] params = parameterMap.get(name); - if (params == null) { - params = new Object[] {}; - } - try { - trySerialization(m.invoke(FILTERS, params)); - } catch (Exception e) { - fail(name + ": " + e); + switch (name) { + case "condition": + assertSerializableFilter(name, FILTERS.condition(FILTERS.pass())); + break; + case "label": + assertSerializableFilter(name, FILTERS.label("label")); + break; + case "fromProto": + assertSerializableFilter(name, FILTERS.label("label").toProto()); + break; + default: + assertSerializableFilter(name, m.invoke(FILTERS)); } } } } - private void trySerialization(Object obj) throws IOException, ClassNotFoundException { - Path serFile = Files.createTempFile("filter", ".ser"); - try (ObjectOutputStream outStream = - new ObjectOutputStream(new FileOutputStream(serFile.toFile()))) { + private void assertSerializableFilter(String name, Object obj) { + try { + assertSerializable(obj); + } catch (IOException | ClassNotFoundException e) { + fail(name + ": " + e); + } + } + + private void assertSerializable(Object obj) throws IOException, ClassNotFoundException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (ObjectOutputStream outStream = new ObjectOutputStream(bos)) { outStream.writeObject(obj); } - try (ObjectInputStream inStream = - new ObjectInputStream(new FileInputStream(serFile.toFile()))) { + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + try (ObjectInputStream inStream = new ObjectInputStream(bis)) { inStream.readObject(); } } From 94ca9ec52cec53bac3810cabda9451145b090a97 Mon Sep 17 00:00:00 2001 From: dmitry-fa Date: Wed, 2 Sep 2020 13:16:32 +0300 Subject: [PATCH 4/7] customize serialization of builders --- .../bigtable/data/v2/models/Filters.java | 19 +++++++++++ .../bigtable/data/v2/models/FiltersTest.java | 34 ++++++++++++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java index 79fc94fa8..4c8260854 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Filters.java @@ -26,6 +26,7 @@ import com.google.common.base.Preconditions; import com.google.protobuf.ByteString; import java.io.IOException; +import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.annotation.Nonnull; @@ -210,6 +211,12 @@ private void writeObject(ObjectOutputStream s) throws IOException { s.writeObject(builder.build()); } + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + RowFilter.Chain chain = (RowFilter.Chain) s.readObject(); + this.builder = chain.toBuilder(); + } + private ChainFilter() { this.builder = RowFilter.Chain.newBuilder(); } @@ -257,6 +264,12 @@ private void writeObject(ObjectOutputStream s) throws IOException { s.writeObject(builder.build()); } + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + RowFilter.Interleave interleave = (RowFilter.Interleave) s.readObject(); + this.builder = interleave.toBuilder(); + } + private InterleaveFilter() { builder = RowFilter.Interleave.newBuilder(); } @@ -303,6 +316,12 @@ private void writeObject(ObjectOutputStream s) throws IOException { s.writeObject(builder.build()); } + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + RowFilter.Condition condition = (RowFilter.Condition) s.readObject(); + this.builder = condition.toBuilder(); + } + private ConditionFilter(@Nonnull Filter predicate) { Preconditions.checkNotNull(predicate); builder = RowFilter.Condition.newBuilder().setPredicateFilter(predicate.toProto()); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java index d1da9bdce..028d6f111 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java @@ -17,6 +17,7 @@ import static com.google.cloud.bigtable.data.v2.models.Filters.FILTERS; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import com.google.bigtable.v2.ColumnRange; @@ -548,30 +549,47 @@ public void serializationTest() throws InvocationTargetException, IllegalAccessE if (Modifier.isPublic(m.getModifiers())) { switch (name) { case "condition": - assertSerializableFilter(name, FILTERS.condition(FILTERS.pass())); + checkSerialization( + name, + FILTERS + .condition( + FILTERS + .chain() + .filter(FILTERS.qualifier().exactMatch("data_plan_10gb")) + .filter(FILTERS.value().exactMatch("true"))) + .then(FILTERS.label("passed-filter")) + .otherwise(FILTERS.label("filtered-out"))); break; case "label": - assertSerializableFilter(name, FILTERS.label("label")); + checkSerialization(name, FILTERS.label("label")); break; case "fromProto": - assertSerializableFilter(name, FILTERS.label("label").toProto()); + checkSerialization(name, FILTERS.label("label").toProto()); break; default: - assertSerializableFilter(name, m.invoke(FILTERS)); + checkSerialization(name, m.invoke(FILTERS)); } } } } - private void assertSerializableFilter(String name, Object obj) { + private void checkSerialization(String name, Object filter) { try { - assertSerializable(obj); + Object deserialized = serializeDeserialize(filter); + if (filter instanceof Filters.Filter) { + RowFilter protoBefore = ((Filters.Filter) filter).toProto(); + RowFilter protoAfter = ((Filters.Filter) deserialized).toProto(); + assertEquals( + "'" + name + "' filter protoBuf mismatch after deserialization", + protoBefore, + protoAfter); + } } catch (IOException | ClassNotFoundException e) { fail(name + ": " + e); } } - private void assertSerializable(Object obj) throws IOException, ClassNotFoundException { + private Object serializeDeserialize(Object obj) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (ObjectOutputStream outStream = new ObjectOutputStream(bos)) { outStream.writeObject(obj); @@ -579,7 +597,7 @@ private void assertSerializable(Object obj) throws IOException, ClassNotFoundExc ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); try (ObjectInputStream inStream = new ObjectInputStream(bis)) { - inStream.readObject(); + return inStream.readObject(); } } } From 9ce579f31b4b99c8cbcc1aac7e8a6899313e8ed0 Mon Sep 17 00:00:00 2001 From: dmitry-fa Date: Wed, 2 Sep 2020 14:47:16 +0300 Subject: [PATCH 5/7] customize serialization of builders --- .../cloud/bigtable/data/v2/models/FiltersTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java index 028d6f111..c8d759019 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java @@ -17,7 +17,7 @@ import static com.google.cloud.bigtable.data.v2.models.Filters.FILTERS; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertEquals; +import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.fail; import com.google.bigtable.v2.ColumnRange; @@ -573,23 +573,23 @@ public void serializationTest() throws InvocationTargetException, IllegalAccessE } } - private void checkSerialization(String name, Object filter) { + private static void checkSerialization(String name, Object filter) { try { Object deserialized = serializeDeserialize(filter); if (filter instanceof Filters.Filter) { RowFilter protoBefore = ((Filters.Filter) filter).toProto(); RowFilter protoAfter = ((Filters.Filter) deserialized).toProto(); - assertEquals( - "'" + name + "' filter protoBuf mismatch after deserialization", - protoBefore, - protoAfter); + assertWithMessage("'" + name + "' filter protoBuf mismatches after deserialization") + .that(protoBefore) + .isEqualTo(protoAfter); } } catch (IOException | ClassNotFoundException e) { fail(name + ": " + e); } } - private Object serializeDeserialize(Object obj) throws IOException, ClassNotFoundException { + private static Object serializeDeserialize(Object obj) + throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (ObjectOutputStream outStream = new ObjectOutputStream(bos)) { outStream.writeObject(obj); From e63841e0d9b25bb00de80bb00b094de001dd28ca Mon Sep 17 00:00:00 2001 From: dmitry-fa Date: Wed, 2 Sep 2020 16:41:37 +0300 Subject: [PATCH 6/7] check serialization of non-filter instances --- .../bigtable/data/v2/models/FiltersTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java index c8d759019..627e42f34 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -576,18 +577,52 @@ public void serializationTest() throws InvocationTargetException, IllegalAccessE private static void checkSerialization(String name, Object filter) { try { Object deserialized = serializeDeserialize(filter); + checkClassDeclaresSerialVersionUid(filter.getClass()); if (filter instanceof Filters.Filter) { RowFilter protoBefore = ((Filters.Filter) filter).toProto(); RowFilter protoAfter = ((Filters.Filter) deserialized).toProto(); assertWithMessage("'" + name + "' filter protoBuf mismatches after deserialization") .that(protoBefore) .isEqualTo(protoAfter); + } else if (filter instanceof RowFilter) { + assertWithMessage("'" + name + "' deserialized filter differs") + .that(filter) + .isEqualTo(deserialized); + } else { + Class cls = filter.getClass(); + checkClassDoesNotContainNonStaticFields(cls, cls.getFields()); + checkClassDoesNotContainNonStaticFields(cls, cls.getDeclaredFields()); } } catch (IOException | ClassNotFoundException e) { fail(name + ": " + e); } } + private static void checkClassDeclaresSerialVersionUid(Class cls) { + String uid = "serialVersionUID"; + for (Field field : cls.getDeclaredFields()) { + if (field.getName() == uid) { + int modifiers = field.getModifiers(); + assertWithMessage(field + " is not static").that(Modifier.isStatic(modifiers)).isTrue(); + assertWithMessage(field + " is not final").that(Modifier.isFinal(modifiers)).isTrue(); + assertWithMessage(field + " is not private").that(Modifier.isPrivate(modifiers)).isTrue(); + assertWithMessage(field + " must be long") + .that(field.getType().getSimpleName()) + .isEqualTo("long"); + return; + } + } + fail(cls + " does not declare serialVersionUID"); + } + + private static void checkClassDoesNotContainNonStaticFields(Class cls, Field[] fields) { + for (Field field : fields) { + assertWithMessage(cls + " has a non-static field '" + field + "'") + .that(Modifier.isStatic(field.getModifiers())) + .isTrue(); + } + } + private static Object serializeDeserialize(Object obj) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); From 2683ee7c724a3bcc6032697d1c82679518373b19 Mon Sep 17 00:00:00 2001 From: dmitry-fa Date: Fri, 18 Sep 2020 11:59:13 +0300 Subject: [PATCH 7/7] test for spawned filters --- .../bigtable/data/v2/models/FiltersTest.java | 123 +++++++++++++++++- 1 file changed, 118 insertions(+), 5 deletions(-) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java index 627e42f34..e5fcd133f 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/FiltersTest.java @@ -579,11 +579,7 @@ private static void checkSerialization(String name, Object filter) { Object deserialized = serializeDeserialize(filter); checkClassDeclaresSerialVersionUid(filter.getClass()); if (filter instanceof Filters.Filter) { - RowFilter protoBefore = ((Filters.Filter) filter).toProto(); - RowFilter protoAfter = ((Filters.Filter) deserialized).toProto(); - assertWithMessage("'" + name + "' filter protoBuf mismatches after deserialization") - .that(protoBefore) - .isEqualTo(protoAfter); + checkFilters(name, (Filters.Filter) filter, (Filters.Filter) deserialized); } else if (filter instanceof RowFilter) { assertWithMessage("'" + name + "' deserialized filter differs") .that(filter) @@ -592,12 +588,129 @@ private static void checkSerialization(String name, Object filter) { Class cls = filter.getClass(); checkClassDoesNotContainNonStaticFields(cls, cls.getFields()); checkClassDoesNotContainNonStaticFields(cls, cls.getDeclaredFields()); + checkSpawnedFilters(name, cls, filter, deserialized); } } catch (IOException | ClassNotFoundException e) { fail(name + ": " + e); } } + private static void checkFilters( + String name, Filters.Filter original, Filters.Filter deserialized) { + RowFilter protoBefore = ((Filters.Filter) original).toProto(); + RowFilter protoAfter = ((Filters.Filter) deserialized).toProto(); + assertWithMessage("'" + name + "' filter protoBuf mismatches after deserialization") + .that(protoBefore) + .isEqualTo(protoAfter); + } + + private static void checkSpawnedFilters( + String name, Class cls, Object original, Object deserialized) { + + int numberOfMethods = 0; + for (Method m : cls.getDeclaredMethods()) { + if (Modifier.isPublic(m.getModifiers())) { + numberOfMethods++; + } + } + ByteString re = ByteString.copyFromUtf8("some\\[0\\-9\\]regex"); + + switch (name) { + case "family": + { + Filters.FamilyFilter f1 = (Filters.FamilyFilter) original; + Filters.FamilyFilter f2 = (Filters.FamilyFilter) deserialized; + + assertThat(numberOfMethods).isEqualTo(2); + checkFilters(name + "/exactMatch", f1.exactMatch("abc"), f2.exactMatch("abc")); + checkFilters(name + "/regex", f1.regex("*"), f2.regex("*")); + + break; + } + case "qualifier": + { + Filters.QualifierFilter f1 = (Filters.QualifierFilter) original; + Filters.QualifierFilter f2 = (Filters.QualifierFilter) deserialized; + + assertThat(numberOfMethods).isEqualTo(5); + checkFilters(name + "/exactMatch", f1.exactMatch("abc"), f2.exactMatch("abc")); + checkFilters(name + "/exactMatch(ByteString)", f1.exactMatch(re), f2.exactMatch(re)); + checkFilters(name + "/regex", f1.regex("*"), f2.regex("*")); + checkFilters(name + "/regex(ByteString)", f1.regex(re), f2.regex(re)); + checkFilters( + name + "/rangeWithinFamily", + f1.rangeWithinFamily("family"), + f2.rangeWithinFamily("family")); + + break; + } + case "limit": + { + Filters.LimitFilter f1 = (Filters.LimitFilter) original; + Filters.LimitFilter f2 = (Filters.LimitFilter) deserialized; + + assertThat(numberOfMethods).isEqualTo(2); + checkFilters( + name + "/cellsPerColumn", f1.cellsPerColumn(100500), f2.cellsPerColumn(100500)); + checkFilters(name + "/cellsPerRow", f1.cellsPerRow(-10), f2.cellsPerRow(-10)); + + break; + } + case "value": + { + Filters.ValueFilter f1 = (Filters.ValueFilter) original; + Filters.ValueFilter f2 = (Filters.ValueFilter) deserialized; + + assertThat(numberOfMethods).isEqualTo(6); + checkFilters(name + "/exactMatch", f1.exactMatch("x"), f2.exactMatch("x")); + checkFilters(name + "/exactMatch(ByteString)", f1.exactMatch(re), f2.exactMatch(re)); + checkFilters(name + "/range", f1.range(), f2.range()); + checkFilters(name + "/regex", f1.regex("*"), f2.regex("*")); + checkFilters(name + "/regex(ByteString)", f1.regex(re), f2.regex(re)); + checkFilters(name + "/strip", f1.strip(), f2.strip()); + + break; + } + case "offset": + { + Filters.OffsetFilter f1 = (Filters.OffsetFilter) original; + Filters.OffsetFilter f2 = (Filters.OffsetFilter) deserialized; + + assertThat(numberOfMethods).isEqualTo(1); + checkFilters(name + "/cellsPerRow", f1.cellsPerRow(100500), f2.cellsPerRow(100500)); + + break; + } + case "key": + { + Filters.KeyFilter f1 = (Filters.KeyFilter) original; + Filters.KeyFilter f2 = (Filters.KeyFilter) deserialized; + + assertThat(numberOfMethods).isEqualTo(5); + checkFilters(name + "/exactMatch", f1.exactMatch("a"), f2.exactMatch("a")); + checkFilters(name + "/exactMatch(ByteString)", f1.exactMatch(re), f2.exactMatch(re)); + checkFilters(name + "/regex", f1.regex("a"), f2.regex("a")); + checkFilters(name + "/regex(ByteString)", f1.regex(re), f2.regex(re)); + checkFilters(name + "/sample", f1.sample(0.1), f2.sample(0.1)); + + break; + } + case "timestamp": + { + Filters.TimestampFilter f1 = (Filters.TimestampFilter) original; + Filters.TimestampFilter f2 = (Filters.TimestampFilter) deserialized; + + assertThat(numberOfMethods).isEqualTo(2); + checkFilters(name + "/exact", f1.exact(100500L), f2.exact(100500L)); + checkFilters(name + "/range", f1.range(), f2.range()); + + break; + } + default: + fail("Untested filter: " + name); + } + } + private static void checkClassDeclaresSerialVersionUid(Class cls) { String uid = "serialVersionUID"; for (Field field : cls.getDeclaredFields()) {