/** * Reads the composite snapshot of all the contained serializers in a way that is compatible * with Version 1 of the deprecated {@link CompositeTypeSerializerConfigSnapshot}. */ public static NestedSerializersSnapshotDelegate legacyReadNestedSerializerSnapshots(DataInputView in, ClassLoader cl) throws IOException { @SuppressWarnings("deprecation") final List<Tuple2<TypeSerializer<?>, TypeSerializerSnapshot<?>>> serializersAndSnapshots = TypeSerializerSerializationUtil.readSerializersAndConfigsWithResilience(in, cl); final TypeSerializerSnapshot<?>[] nestedSnapshots = serializersAndSnapshots.stream() .map(t -> t.f1) .toArray(TypeSerializerSnapshot<?>[]::new); return new NestedSerializersSnapshotDelegate(nestedSnapshots); }
/** * Reads from a data input view a {@link TypeSerializer} that was previously * written using {@link #writeSerializer(DataOutputView, TypeSerializer)}. * * <p>If deserialization fails for any reason (corrupted serializer bytes, serializer class * no longer in classpath, serializer class no longer valid, etc.), an {@link IOException} is thrown. * * @param in the data input view. * @param userCodeClassLoader the user code class loader to use. * * @param <T> Data type of the serializer. * * @return the deserialized serializer. */ public static <T> TypeSerializer<T> tryReadSerializer(DataInputView in, ClassLoader userCodeClassLoader) throws IOException { return tryReadSerializer(in, userCodeClassLoader, false); }
@Override public void write(DataOutputView out) throws IOException { super.write(out); TypeSerializerSerializationUtil.writeSerializersAndConfigsWithResilience(out, nestedSerializersAndConfigs); }
/** * Verifies that serializers of anonymous classes can be deserialized, even if serialVersionUID changes. */ @Test public void testAnonymousSerializerClassWithChangedSerialVersionUID() throws Exception { TypeSerializer anonymousClassSerializer = new AbstractIntSerializer() {}; // assert that our assumption holds Assert.assertTrue(anonymousClassSerializer.getClass().isAnonymousClass()); byte[] anonymousSerializerBytes; try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { TypeSerializerSerializationUtil.writeSerializer(new DataOutputViewStreamWrapper(out), anonymousClassSerializer); anonymousSerializerBytes = out.toByteArray(); } long newSerialVersionUID = 1234567L; // assert that we're actually modifying to a different serialVersionUID Assert.assertNotEquals(ObjectStreamClass.lookup(anonymousClassSerializer.getClass()).getSerialVersionUID(), newSerialVersionUID); modifySerialVersionUID(anonymousSerializerBytes, anonymousClassSerializer.getClass().getName(), newSerialVersionUID); try (ByteArrayInputStream in = new ByteArrayInputStream(anonymousSerializerBytes)) { anonymousClassSerializer = TypeSerializerSerializationUtil.tryReadSerializer(new DataInputViewStreamWrapper(in), Thread.currentThread().getContextClassLoader()); } // serializer should have been deserialized despite serialVersionUID mismatch Assert.assertNotNull(anonymousClassSerializer); Assert.assertTrue(anonymousClassSerializer.getClass().isAnonymousClass()); }
/** * Verifies resilience to serializer deserialization failures when writing and reading * serializer and config snapshot pairs. */ @Test public void testSerializerAndConfigPairsSerializationWithSerializerDeserializationFailures() throws Exception { TestIntSerializer serializer = new TestIntSerializer(); List<Tuple2<TypeSerializer<?>, TypeSerializerSnapshot<?>>> serializersAndConfigs = Arrays.asList( new Tuple2<TypeSerializer<?>, TypeSerializerSnapshot<?>>( serializer, serializer.snapshotConfiguration())); byte[] serializedSerializersAndConfigs; try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { TypeSerializerSerializationUtil.writeSerializersAndConfigsWithResilience( new DataOutputViewStreamWrapper(out), serializersAndConfigs); serializedSerializersAndConfigs = out.toByteArray(); } Set<String> cnfThrowingClassnames = new HashSet<>(); cnfThrowingClassnames.add(TestIntSerializer.class.getName()); List<Tuple2<TypeSerializer<?>, TypeSerializerSnapshot<?>>> restored; try (ByteArrayInputStream in = new ByteArrayInputStream(serializedSerializersAndConfigs)) { restored = TypeSerializerSerializationUtil.readSerializersAndConfigsWithResilience( new DataInputViewStreamWrapper(in), new ArtificialCNFExceptionThrowingClassLoader( Thread.currentThread().getContextClassLoader(), cnfThrowingClassnames)); } Assert.assertEquals(1, restored.size()); Assert.assertTrue(restored.get(0).f0 instanceof UnloadableDummyTypeSerializer); Assert.assertThat(restored.get(0).f1, Matchers.instanceOf(SimpleTypeSerializerSnapshot.class)); }
@SuppressWarnings("unchecked") @Override public void read(DataInputView in) throws IOException { super.read(in); // only starting from version 3, we have the key serializer and its config snapshot written if (getReadVersion() >= 3) { Tuple2<TypeSerializer<?>, TypeSerializerConfigSnapshot> keySerializerAndConfig = TypeSerializerSerializationUtil.readSerializersAndConfigsWithResilience(in, userCodeClassLoader).get(0); this.keySerializer = (TypeSerializer<K>) keySerializerAndConfig.f0; this.keySerializerConfigSnapshot = keySerializerAndConfig.f1; } else { this.keySerializer = TypeSerializerSerializationUtil.tryReadSerializer(in, userCodeClassLoader); this.keySerializerConfigSnapshot = null; } int numKvStates = in.readShort(); stateMetaInfoSnapshots = new ArrayList<>(numKvStates); for (int i = 0; i < numKvStates; i++) { stateMetaInfoSnapshots.add( KeyedBackendStateMetaInfoSnapshotReaderWriters .getReaderForVersion(getReadVersion(), userCodeClassLoader) .readStateMetaInfo(in)); } } }
@Override public final void writeSnapshot(DataOutputView out) throws IOException { checkState(serializer != null, "the prior serializer has not been set on this"); // write the snapshot for a non-updated serializer. // this mimics the previous behavior where the TypeSerializer was // Java-serialized, for backwards compatibility TypeSerializerSerializationUtil.writeSerializer(out, serializer); // now delegate to the snapshots own writing code write(out); }
serializer = tryReadSerializer(bufferWrapper, userCodeClassLoader, true); configSnapshot = readSerializerConfigSnapshot(bufferWrapper, userCodeClassLoader);
TypeSerializerSerializationUtil.writeSerializer(outViewWrapper, entry.getValue().f0); TypeSerializerSerializationUtil.writeSerializerConfigSnapshot(outViewWrapper, entry.getValue().f1); TypeSerializerSerializationUtil.writeSerializer(outViewWrapper, entry.getValue().f0); TypeSerializerSerializationUtil.writeSerializerConfigSnapshot(outViewWrapper, entry.getValue().f1); TypeSerializerSerializationUtil.writeSerializer(outViewWrapper, entry.getValue().f0); TypeSerializerSerializationUtil.writeSerializerConfigSnapshot(outViewWrapper, entry.getValue().f1);
} else if (readVersion >= 3) { Tuple2<TypeSerializer<?>, TypeSerializerSnapshot<?>> keySerializerAndConfig = TypeSerializerSerializationUtil.readSerializersAndConfigsWithResilience(in, userCodeClassLoader).get(0); this.keySerializerConfigSnapshot = (TypeSerializerSnapshot<K>) keySerializerAndConfig.f1; } else { this.keySerializerConfigSnapshot = new BackwardsCompatibleSerializerSnapshot<>( TypeSerializerSerializationUtil.tryReadSerializer(in, userCodeClassLoader, true));
/** * Verifies that reading and writing serializers work correctly. */ @Test public void testSerializerSerialization() throws Exception { TypeSerializer<?> serializer = IntSerializer.INSTANCE; byte[] serialized; try (ByteArrayOutputStreamWithPos out = new ByteArrayOutputStreamWithPos()) { TypeSerializerSerializationUtil.writeSerializer(new DataOutputViewStreamWrapper(out), serializer); serialized = out.toByteArray(); } TypeSerializer<?> deserializedSerializer; try (ByteArrayInputStreamWithPos in = new ByteArrayInputStreamWithPos(serialized)) { deserializedSerializer = TypeSerializerSerializationUtil.tryReadSerializer( new DataInputViewStreamWrapper(in), Thread.currentThread().getContextClassLoader()); } Assert.assertEquals(serializer, deserializedSerializer); }
TypeSerializerSerializationUtil.writeSerializer(outViewWrapper, entry.getValue().f0); TypeSerializerSerializationUtil.writeSerializer(outViewWrapper, entry.getValue().f0); TypeSerializerSerializationUtil.writeSerializer(outViewWrapper, entry.getValue().f0);
fieldSerializer = TypeSerializerSerializationUtil.tryReadSerializer(inViewWrapper, getUserCodeClassLoader(), true); fieldSerializerConfigSnapshot = TypeSerializerSerializationUtil.readSerializerConfigSnapshot(inViewWrapper, getUserCodeClassLoader()); registeredSubclassSerializer = TypeSerializerSerializationUtil.tryReadSerializer(inViewWrapper, getUserCodeClassLoader(), true); registeredSubclassSerializerConfigSnapshot = TypeSerializerSerializationUtil.readSerializerConfigSnapshot(inViewWrapper, getUserCodeClassLoader()); cachedSubclassSerializer = TypeSerializerSerializationUtil.tryReadSerializer(inViewWrapper, getUserCodeClassLoader(), true); cachedSubclassSerializerConfigSnapshot = TypeSerializerSerializationUtil.readSerializerConfigSnapshot(inViewWrapper, getUserCodeClassLoader());
for (Tuple2<TypeSerializer<?>, TypeSerializerConfigSnapshot> serAndConfSnapshot : serializersAndConfigs) { out.writeInt(bufferWithPos.getPosition()); writeSerializer(bufferWrapper, serAndConfSnapshot.f0); writeSerializerConfigSnapshot(bufferWrapper, serAndConfSnapshot.f1);
@Override public void read(DataInputView in) throws IOException { super.read(in); this.nestedSerializersAndConfigs = TypeSerializerSerializationUtil.readSerializersAndConfigsWithResilience(in, getUserCodeClassLoader()); }
} else if (readVersion >= 3) { Tuple2<TypeSerializer<?>, TypeSerializerSnapshot<?>> keySerializerAndConfig = TypeSerializerSerializationUtil.readSerializersAndConfigsWithResilience(in, userCodeClassLoader).get(0); this.keySerializerConfigSnapshot = (TypeSerializerSnapshot<K>) keySerializerAndConfig.f1; } else { this.keySerializerConfigSnapshot = new BackwardsCompatibleSerializerSnapshot<>( TypeSerializerSerializationUtil.tryReadSerializer(in, userCodeClassLoader, true));
@Override public final void readSnapshot(int readVersion, DataInputView in, ClassLoader userCodeClassLoader) throws IOException { if (readVersion != ADAPTER_VERSION) { throw new IOException("Wrong/unexpected version for the TypeSerializerConfigSnapshot: " + readVersion); } serializer = TypeSerializerSerializationUtil.tryReadSerializer(in, userCodeClassLoader, true); // now delegate to the snapshots own reading code setUserCodeClassLoader(userCodeClassLoader); read(in); }
@Override protected void writeKeyAndNamespaceSerializers(DataOutputView out) throws IOException { // write key / namespace serializers, and their configuration snapshots TypeSerializerSerializationUtil.writeSerializersAndConfigsWithResilience( out, Arrays.asList( Tuple2.of(timersSnapshot.getKeySerializer(), timersSnapshot.getKeySerializerConfigSnapshot()), Tuple2.of(timersSnapshot.getNamespaceSerializer(), timersSnapshot.getNamespaceSerializerConfigSnapshot()))); } }
/** * Verifies deserialization failure cases when reading a serializer from bytes, in the * case of a {@link InvalidClassException}. */ @Test public void testSerializerSerializationWithInvalidClass() throws Exception { TypeSerializer<?> serializer = IntSerializer.INSTANCE; byte[] serialized; try (ByteArrayOutputStreamWithPos out = new ByteArrayOutputStreamWithPos()) { TypeSerializerSerializationUtil.writeSerializer(new DataOutputViewStreamWrapper(out), serializer); serialized = out.toByteArray(); } TypeSerializer<?> deserializedSerializer; try (ByteArrayInputStreamWithPos in = new ByteArrayInputStreamWithPos(serialized)) { deserializedSerializer = TypeSerializerSerializationUtil.tryReadSerializer( new DataInputViewStreamWrapper(in), new ArtificialCNFExceptionThrowingClassLoader( Thread.currentThread().getContextClassLoader(), Collections.singleton(IntSerializer.class.getName())), true); } Assert.assertTrue(deserializedSerializer instanceof UnloadableDummyTypeSerializer); }
for (Tuple2<TypeSerializer<?>, TypeSerializerSnapshot<?>> serAndConfSnapshot : serializersAndConfigs) { out.writeInt(bufferWithPos.getPosition()); writeSerializer(bufferWrapper, serAndConfSnapshot.f0);