All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.cloud.dataflow.sdk.util.MutationDetectors Maven / Gradle / Ivy

Go to download

Google Cloud Dataflow Java SDK provides a simple, Java-based interface for processing virtually any size data using Google cloud resources. This artifact includes entire Dataflow Java SDK.

There is a newer version: 2.5.0
Show newest version
/*
 * Copyright (C) 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.cloud.dataflow.sdk.util;

import com.google.cloud.dataflow.sdk.coders.Coder;
import com.google.cloud.dataflow.sdk.coders.CoderException;

import java.util.Arrays;
import java.util.Objects;

/**
 * Static methods for creating and working with {@link MutationDetector}.
 */
public class MutationDetectors {

  private MutationDetectors() {}

  /**
     * Creates a new {@code MutationDetector} for the provided {@code value} that uses the provided
     * {@link Coder} to perform deep copies and comparisons by serializing and deserializing values.
     *
     * 

It is permissible for {@code value} to be {@code null}. Since {@code null} is immutable, * the mutation check will always succeed. */ public static MutationDetector forValueWithCoder(T value, Coder coder) throws CoderException { if (value == null) { return noopMutationDetector(); } else { return new CodedValueMutationDetector<>(value, coder); } } /** * Creates a new {@code MutationDetector} that always succeeds. * *

This is useful, for example, for providing a very efficient mutation detector for a value * which is already immutable by design. */ public static MutationDetector noopMutationDetector() { return new NoopMutationDetector(); } /** * A {@link MutationDetector} for {@code null}, which is immutable. */ private static class NoopMutationDetector implements MutationDetector { @Override public void verifyUnmodified() { } @Override public void close() { } } /** * Given a value of type {@code T} and a {@link Coder} for that type, provides facilities to save * check that the value has not changed. * * @param the type of values checked for mutation */ private static class CodedValueMutationDetector implements MutationDetector { private final Coder coder; /** * A saved pointer to an in-memory value provided upon construction, which we will check for * forbidden mutations. */ private final T possiblyModifiedObject; /** * A saved encoded copy of the same value as {@link #possiblyModifiedObject}. Naturally, it * will not change if {@link #possiblyModifiedObject} is mutated. */ private final byte[] encodedOriginalObject; /** * The object decoded from {@link #encodedOriginalObject}. It will be used during every call to * {@link #verifyUnmodified}, which could be called many times throughout the lifetime of this * {@link CodedValueMutationDetector}. */ private final T clonedOriginalObject; /** * Create a mutation detector for the provided {@code value}, using the provided {@link Coder} * for cloning and checking serialized forms for equality. */ public CodedValueMutationDetector(T value, Coder coder) throws CoderException { this.coder = coder; this.possiblyModifiedObject = value; this.encodedOriginalObject = CoderUtils.encodeToByteArray(coder, value); this.clonedOriginalObject = CoderUtils.decodeFromByteArray(coder, encodedOriginalObject); } @Override public void verifyUnmodified() { try { verifyUnmodifiedThrowingCheckedExceptions(); } catch (CoderException exn) { throw new RuntimeException(exn); } } private void verifyUnmodifiedThrowingCheckedExceptions() throws CoderException { // If either object believes they are equal, we trust that and short-circuit deeper checks. if (Objects.equals(possiblyModifiedObject, clonedOriginalObject) || Objects.equals(clonedOriginalObject, possiblyModifiedObject)) { return; } // Since retainedObject is in general an instance of a subclass of T, when it is cloned to // clonedObject using a Coder, the two will generally be equivalent viewed as a T, but in // general neither retainedObject.equals(clonedObject) nor clonedObject.equals(retainedObject) // will hold. // // For example, CoderUtils.clone(IterableCoder, IterableSubclass) will // produce an ArrayList with the same contents as the IterableSubclass, but the // latter will quite reasonably not consider itself equivalent to an ArrayList (and vice // versa). // // To enable a reasonable comparison, we clone retainedObject again here, converting it to // the same sort of T that the Coder output when it created clonedObject. T clonedPossiblyModifiedObject = CoderUtils.clone(coder, possiblyModifiedObject); // If deepEquals() then we trust the equals implementation. // This deliberately allows fields to escape this check. if (Objects.deepEquals(clonedPossiblyModifiedObject, clonedOriginalObject)) { return; } // If not deepEquals(), the class may just have a poor equals() implementation. // So we next try checking their serialized forms. We re-serialize instead of checking // encodedObject, because the Coder may treat it differently. // // For example, an unbounded Iterable will be encoded in an unbounded way, but decoded into an // ArrayList, which will then be re-encoded in a bounded format. So we really do need to // encode-decode-encode retainedObject. if (Arrays.equals( CoderUtils.encodeToByteArray(coder, clonedOriginalObject), CoderUtils.encodeToByteArray(coder, clonedPossiblyModifiedObject))) { return; } // If we got here, then they are not deepEquals() and do not have deepEquals() encodings. // Even if there is some conceptual sense in which the objects are equivalent, it has not // been adequately expressed in code. illegalMutation(clonedOriginalObject, clonedPossiblyModifiedObject); } private void illegalMutation(T previousValue, T newValue) throws CoderException { throw new IllegalMutationException( String.format("Value %s mutated illegally, new value was %s." + " Encoding was %s, now %s.", previousValue, newValue, CoderUtils.encodeToBase64(coder, previousValue), CoderUtils.encodeToBase64(coder, newValue)), previousValue, newValue); } @Override public void close() { verifyUnmodified(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy