beam-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From da...@apache.org
Subject [10/74] [partial] incubator-beam git commit: Rename com/google/cloud/dataflow->org/apache/beam
Date Thu, 14 Apr 2016 04:47:57 GMT
http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/0393a791/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptions.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptions.java b/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptions.java
deleted file mode 100644
index 2ca845d..0000000
--- a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptions.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.cloud.dataflow.sdk.options;
-
-import com.google.auto.service.AutoService;
-import com.google.cloud.dataflow.sdk.Pipeline;
-import com.google.cloud.dataflow.sdk.options.GoogleApiDebugOptions.GoogleApiTracer;
-import com.google.cloud.dataflow.sdk.options.ProxyInvocationHandler.Deserializer;
-import com.google.cloud.dataflow.sdk.options.ProxyInvocationHandler.Serializer;
-import com.google.cloud.dataflow.sdk.runners.DirectPipelineRunner;
-import com.google.cloud.dataflow.sdk.runners.PipelineRunner;
-import com.google.cloud.dataflow.sdk.transforms.DoFn;
-import com.google.cloud.dataflow.sdk.transforms.DoFn.Context;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-import java.lang.reflect.Proxy;
-import java.util.ServiceLoader;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * PipelineOptions are used to configure Pipelines. You can extend {@link PipelineOptions}
- * to create custom configuration options specific to your {@link Pipeline},
- * for both local execution and execution via a {@link PipelineRunner}.
- *
- * <p>{@link PipelineOptions} and their subinterfaces represent a collection of properties
- * which can be manipulated in a type safe manner. {@link PipelineOptions} is backed by a
- * dynamic {@link Proxy} which allows for type safe manipulation of properties in an extensible
- * fashion through plain old Java interfaces.
- *
- * <p>{@link PipelineOptions} can be created with {@link PipelineOptionsFactory#create()}
- * and {@link PipelineOptionsFactory#as(Class)}. They can be created
- * from command-line arguments with {@link PipelineOptionsFactory#fromArgs(String[])}.
- * They can be converted to another type by invoking {@link PipelineOptions#as(Class)} and
- * can be accessed from within a {@link DoFn} by invoking
- * {@link Context#getPipelineOptions()}.
- *
- * <p>For example:
- * <pre>{@code
- * // The most common way to construct PipelineOptions is via command-line argument parsing:
- * public static void main(String[] args) {
- *   // Will parse the arguments passed into the application and construct a PipelineOptions
- *   // Note that --help will print registered options, and --help=PipelineOptionsClassName
- *   // will print out usage for the specific class.
- *   PipelineOptions options =
- *       PipelineOptionsFactory.fromArgs(args).create();
- *
- *   Pipeline p = Pipeline.create(options);
- *   ...
- *   p.run();
- * }
- *
- * // To create options for the DirectPipeline:
- * DirectPipelineOptions directPipelineOptions =
- *     PipelineOptionsFactory.as(DirectPipelineOptions.class);
- * directPipelineOptions.setStreaming(true);
- *
- * // To cast from one type to another using the as(Class) method:
- * DataflowPipelineOptions dataflowPipelineOptions =
- *     directPipelineOptions.as(DataflowPipelineOptions.class);
- *
- * // Options for the same property are shared between types
- * // The statement below will print out "true"
- * System.out.println(dataflowPipelineOptions.isStreaming());
- *
- * // Prints out registered options.
- * PipelineOptionsFactory.printHelp(System.out);
- *
- * // Prints out options which are available to be set on DataflowPipelineOptions
- * PipelineOptionsFactory.printHelp(System.out, DataflowPipelineOptions.class);
- * }</pre>
- *
- * <h2>Defining Your Own PipelineOptions</h2>
- *
- * Defining your own {@link PipelineOptions} is the way for you to make configuration
- * options available for both local execution and execution via a {@link PipelineRunner}.
- * By having PipelineOptionsFactory as your command-line interpreter, you will provide
- * a standardized way for users to interact with your application via the command-line.
- *
- * <p>To define your own {@link PipelineOptions}, you create an interface which
- * extends {@link PipelineOptions} and define getter/setter pairs. These
- * getter/setter pairs define a collection of
- * <a href="https://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html">
- * JavaBean properties</a>.
- *
- * <p>For example:
- * <pre>{@code
- *  // Creates a user defined property called "myProperty"
- *  public interface MyOptions extends PipelineOptions {
- *    String getMyProperty();
- *    void setMyProperty(String value);
- *  }
- * }</pre>
- *
- * <p>Note: Please see the section on Registration below when using custom property types.
- *
- * <h3>Restrictions</h3>
- *
- * Since PipelineOptions can be "cast" to multiple types dynamically using
- * {@link PipelineOptions#as(Class)}, a property must conform to the following set of restrictions:
- * <ul>
- *   <li>Any property with the same name must have the same return type for all derived
- *       interfaces of {@link PipelineOptions}.
- *   <li>Every bean property of any interface derived from {@link PipelineOptions} must have a
- *       getter and setter method.
- *   <li>Every method must conform to being a getter or setter for a JavaBean.
- *   <li>The derived interface of {@link PipelineOptions} must be composable with every interface
- *       part registered with the PipelineOptionsFactory.
- *   <li>Only getters may be annotated with {@link JsonIgnore @JsonIgnore}.
- *   <li>If any getter is annotated with {@link JsonIgnore @JsonIgnore}, then all getters for
- *       this property must be annotated with {@link JsonIgnore @JsonIgnore}.
- * </ul>
- *
- * <h3>Annotations For PipelineOptions</h3>
- *
- * {@link Description @Description} can be used to annotate an interface or a getter
- * with useful information which is output when {@code --help}
- * is invoked via {@link PipelineOptionsFactory#fromArgs(String[])}.
- *
- * <p>{@link Default @Default} represents a set of annotations that can be used to annotate getter
- * properties on {@link PipelineOptions} with information representing the default value to be
- * returned if no value is specified. Any default implementation (using the {@code default} keyword)
- * is ignored.
- *
- * <p>{@link Hidden @Hidden} hides an option from being listed when {@code --help}
- * is invoked via {@link PipelineOptionsFactory#fromArgs(String[])}.
- *
- * <p>{@link Validation @Validation} represents a set of annotations that can be used to annotate
- * getter properties on {@link PipelineOptions} with information representing the validation
- * criteria to be used when validating with the {@link PipelineOptionsValidator}. Validation
- * will be performed if during construction of the {@link PipelineOptions},
- * {@link PipelineOptionsFactory#withValidation()} is invoked.
- *
- * <p>{@link JsonIgnore @JsonIgnore} is used to prevent a property from being serialized and
- * available during execution of {@link DoFn}. See the Serialization section below for more
- * details.
- *
- * <h2>Registration Of PipelineOptions</h2>
- *
- * Registration of {@link PipelineOptions} by an application guarantees that the
- * {@link PipelineOptions} is composable during execution of their {@link Pipeline} and
- * meets the restrictions listed above or will fail during registration. Registration
- * also lists the registered {@link PipelineOptions} when {@code --help}
- * is invoked via {@link PipelineOptionsFactory#fromArgs(String[])}.
- *
- * <p>Registration can be performed by invoking {@link PipelineOptionsFactory#register} within
- * a users application or via automatic registration by creating a {@link ServiceLoader} entry
- * and a concrete implementation of the {@link PipelineOptionsRegistrar} interface.
- *
- * <p>It is optional but recommended to use one of the many build time tools such as
- * {@link AutoService} to generate the necessary META-INF files automatically.
- *
- * <p>A list of registered options can be fetched from
- * {@link PipelineOptionsFactory#getRegisteredOptions()}.
- *
- * <h2>Serialization Of PipelineOptions</h2>
- *
- * {@link PipelineRunner}s require support for options to be serialized. Each property
- * within {@link PipelineOptions} must be able to be serialized using Jackson's
- * {@link ObjectMapper} or the getter method for the property annotated with
- * {@link JsonIgnore @JsonIgnore}.
- *
- * <p>Jackson supports serialization of many types and supports a useful set of
- * <a href="https://github.com/FasterXML/jackson-annotations">annotations</a> to aid in
- * serialization of custom types. We point you to the public
- * <a href="https://github.com/FasterXML/jackson">Jackson documentation</a> when attempting
- * to add serialization support for your custom types. See {@link GoogleApiTracer} for an
- * example using the Jackson annotations to serialize and deserialize a custom type.
- *
- * <p>Note: It is an error to have the same property available in multiple interfaces with only
- * some of them being annotated with {@link JsonIgnore @JsonIgnore}. It is also an error to mark a
- * setter for a property with {@link JsonIgnore @JsonIgnore}.
- */
-@JsonSerialize(using = Serializer.class)
-@JsonDeserialize(using = Deserializer.class)
-@ThreadSafe
-public interface PipelineOptions {
-  /**
-   * Transforms this object into an object of type {@code <T>} saving each property
-   * that has been manipulated. {@code <T>} must extend {@link PipelineOptions}.
-   *
-   * <p>If {@code <T>} is not registered with the {@link PipelineOptionsFactory}, then we
-   * attempt to verify that {@code <T>} is composable with every interface that this
-   * instance of the {@code PipelineOptions} has seen.
-   *
-   * @param kls The class of the type to transform to.
-   * @return An object of type kls.
-   */
-  <T extends PipelineOptions> T as(Class<T> kls);
-
-  /**
-   * Makes a deep clone of this object, and transforms the cloned object into the specified
-   * type {@code kls}. See {@link #as} for more information about the conversion.
-   *
-   * <p>Properties that are marked with {@code @JsonIgnore} will not be cloned.
-   */
-  <T extends PipelineOptions> T cloneAs(Class<T> kls);
-
-  /**
-   * The pipeline runner that will be used to execute the pipeline.
-   * For registered runners, the class name can be specified, otherwise the fully
-   * qualified name needs to be specified.
-   */
-  @Validation.Required
-  @Description("The pipeline runner that will be used to execute the pipeline. "
-      + "For registered runners, the class name can be specified, otherwise the fully "
-      + "qualified name needs to be specified.")
-  @Default.Class(DirectPipelineRunner.class)
-  Class<? extends PipelineRunner<?>> getRunner();
-  void setRunner(Class<? extends PipelineRunner<?>> kls);
-
-  /**
-   * Enumeration of the possible states for a given check.
-   */
-  public static enum CheckEnabled {
-    OFF,
-    WARNING,
-    ERROR;
-  }
-
-  /**
-   * Whether to check for stable unique names on each transform. This is necessary to
-   * support updating of pipelines.
-   */
-  @Validation.Required
-  @Description("Whether to check for stable unique names on each transform. This is necessary to "
-      + "support updating of pipelines.")
-  @Default.Enum("WARNING")
-  CheckEnabled getStableUniqueNames();
-  void setStableUniqueNames(CheckEnabled enabled);
-
-  /**
-   * A pipeline level default location for storing temporary files.
-   *
-   * <p>This can be a path of any file system.
-   *
-   * <p>{@link #getTempLocation()} can be used as a default location in other
-   * {@link PipelineOptions}.
-   *
-   * <p>If it is unset, {@link PipelineRunner} can override it.
-   */
-  @Description("A pipeline level default location for storing temporary files.")
-  String getTempLocation();
-  void setTempLocation(String value);
-}

http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/0393a791/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactory.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactory.java b/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactory.java
deleted file mode 100644
index dac7726..0000000
--- a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactory.java
+++ /dev/null
@@ -1,1490 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.cloud.dataflow.sdk.options;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.cloud.dataflow.sdk.options.Validation.Required;
-import com.google.cloud.dataflow.sdk.runners.PipelineRunner;
-import com.google.cloud.dataflow.sdk.runners.PipelineRunnerRegistrar;
-import com.google.cloud.dataflow.sdk.util.StringUtils;
-import com.google.cloud.dataflow.sdk.util.common.ReflectHelpers;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Strings;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.collect.SortedSetMultimap;
-import com.google.common.collect.TreeMultimap;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Proxy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.ServiceLoader;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import javax.annotation.Nullable;
-
-/**
- * Constructs a {@link PipelineOptions} or any derived interface that is composable to any other
- * derived interface of {@link PipelineOptions} via the {@link PipelineOptions#as} method. Being
- * able to compose one derived interface of {@link PipelineOptions} to another has the following
- * restrictions:
- * <ul>
- *   <li>Any property with the same name must have the same return type for all derived interfaces
- *       of {@link PipelineOptions}.
- *   <li>Every bean property of any interface derived from {@link PipelineOptions} must have a
- *       getter and setter method.
- *   <li>Every method must conform to being a getter or setter for a JavaBean.
- *   <li>The derived interface of {@link PipelineOptions} must be composable with every interface
- *       registered with this factory.
- * </ul>
- *
- * <p>See the <a
- * href="http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html">JavaBeans
- * specification</a> for more details as to what constitutes a property.
- */
-public class PipelineOptionsFactory {
-  /**
-   * Creates and returns an object that implements {@link PipelineOptions}.
-   * This sets the {@link ApplicationNameOptions#getAppName() "appName"} to the calling
-   * {@link Class#getSimpleName() classes simple name}.
-   *
-   * @return An object that implements {@link PipelineOptions}.
-   */
-  public static PipelineOptions create() {
-    return new Builder().as(PipelineOptions.class);
-  }
-
-  /**
-   * Creates and returns an object that implements {@code <T>}.
-   * This sets the {@link ApplicationNameOptions#getAppName() "appName"} to the calling
-   * {@link Class#getSimpleName() classes simple name}.
-   *
-   * <p>Note that {@code <T>} must be composable with every registered interface with this factory.
-   * See {@link PipelineOptionsFactory#validateWellFormed(Class, Set)} for more details.
-   *
-   * @return An object that implements {@code <T>}.
-   */
-  public static <T extends PipelineOptions> T as(Class<T> klass) {
-    return new Builder().as(klass);
-  }
-
-  /**
-   * Sets the command line arguments to parse when constructing the {@link PipelineOptions}.
-   *
-   * <p>Example GNU style command line arguments:
-   * <pre>
-   *   --project=MyProject (simple property, will set the "project" property to "MyProject")
-   *   --readOnly=true (for boolean properties, will set the "readOnly" property to "true")
-   *   --readOnly (shorthand for boolean properties, will set the "readOnly" property to "true")
-   *   --x=1 --x=2 --x=3 (list style simple property, will set the "x" property to [1, 2, 3])
-   *   --x=1,2,3 (shorthand list style simple property, will set the "x" property to [1, 2, 3])
-   *   --complexObject='{"key1":"value1",...} (JSON format for all other complex types)
-   * </pre>
-   *
-   * <p>Simple properties are able to bound to {@link String}, {@link Class}, enums and Java
-   * primitives {@code boolean}, {@code byte}, {@code short}, {@code int}, {@code long},
-   * {@code float}, {@code double} and their primitive wrapper classes.
-   *
-   * <p>Simple list style properties are able to be bound to {@code boolean[]}, {@code char[]},
-   * {@code short[]}, {@code int[]}, {@code long[]}, {@code float[]}, {@code double[]},
-   * {@code Class[]}, enum arrays, {@code String[]}, and {@code List<String>}.
-   *
-   * <p>JSON format is required for all other types.
-   *
-   * <p>By default, strict parsing is enabled and arguments must conform to be either
-   * {@code --booleanArgName} or {@code --argName=argValue}. Strict parsing can be disabled with
-   * {@link Builder#withoutStrictParsing()}. Empty or null arguments will be ignored whether
-   * or not strict parsing is enabled.
-   *
-   * <p>Help information can be output to {@link System#out} by specifying {@code --help} as an
-   * argument. After help is printed, the application will exit. Specifying only {@code --help}
-   * will print out the list of
-   * {@link PipelineOptionsFactory#getRegisteredOptions() registered options}
-   * by invoking {@link PipelineOptionsFactory#printHelp(PrintStream)}. Specifying
-   * {@code --help=PipelineOptionsClassName} will print out detailed usage information about the
-   * specifically requested PipelineOptions by invoking
-   * {@link PipelineOptionsFactory#printHelp(PrintStream, Class)}.
-   */
-  public static Builder fromArgs(String[] args) {
-    return new Builder().fromArgs(args);
-  }
-
-  /**
-   * After creation we will validate that {@code <T>} conforms to all the
-   * validation criteria. See
-   * {@link PipelineOptionsValidator#validate(Class, PipelineOptions)} for more details about
-   * validation.
-   */
-  public Builder withValidation() {
-    return new Builder().withValidation();
-  }
-
-  /** A fluent {@link PipelineOptions} builder. */
-  public static class Builder {
-    private final String defaultAppName;
-    private final String[] args;
-    private final boolean validation;
-    private final boolean strictParsing;
-
-    // Do not allow direct instantiation
-    private Builder() {
-      this(null, false, true);
-    }
-
-    private Builder(String[] args, boolean validation,
-        boolean strictParsing) {
-      this.defaultAppName = findCallersClassName();
-      this.args = args;
-      this.validation = validation;
-      this.strictParsing = strictParsing;
-    }
-
-    /**
-     * Sets the command line arguments to parse when constructing the {@link PipelineOptions}.
-     *
-     * <p>Example GNU style command line arguments:
-     * <pre>
-     *   --project=MyProject (simple property, will set the "project" property to "MyProject")
-     *   --readOnly=true (for boolean properties, will set the "readOnly" property to "true")
-     *   --readOnly (shorthand for boolean properties, will set the "readOnly" property to "true")
-     *   --x=1 --x=2 --x=3 (list style simple property, will set the "x" property to [1, 2, 3])
-     *   --x=1,2,3 (shorthand list style simple property, will set the "x" property to [1, 2, 3])
-     *   --complexObject='{"key1":"value1",...} (JSON format for all other complex types)
-     * </pre>
-     *
-     * <p>Simple properties are able to bound to {@link String}, {@link Class}, enums and Java
-     * primitives {@code boolean}, {@code byte}, {@code short}, {@code int}, {@code long},
-     * {@code float}, {@code double} and their primitive wrapper classes.
-     *
-     * <p>Simple list style properties are able to be bound to {@code boolean[]}, {@code char[]},
-     * {@code short[]}, {@code int[]}, {@code long[]}, {@code float[]}, {@code double[]},
-     * {@code Class[]}, enum arrays, {@code String[]}, and {@code List<String>}.
-     *
-     * <p>JSON format is required for all other types.
-     *
-     * <p>By default, strict parsing is enabled and arguments must conform to be either
-     * {@code --booleanArgName} or {@code --argName=argValue}. Strict parsing can be disabled with
-     * {@link Builder#withoutStrictParsing()}. Empty or null arguments will be ignored whether
-     * or not strict parsing is enabled.
-     *
-     * <p>Help information can be output to {@link System#out} by specifying {@code --help} as an
-     * argument. After help is printed, the application will exit. Specifying only {@code --help}
-     * will print out the list of
-     * {@link PipelineOptionsFactory#getRegisteredOptions() registered options}
-     * by invoking {@link PipelineOptionsFactory#printHelp(PrintStream)}. Specifying
-     * {@code --help=PipelineOptionsClassName} will print out detailed usage information about the
-     * specifically requested PipelineOptions by invoking
-     * {@link PipelineOptionsFactory#printHelp(PrintStream, Class)}.
-     */
-    public Builder fromArgs(String[] args) {
-      Preconditions.checkNotNull(args, "Arguments should not be null.");
-      return new Builder(args, validation, strictParsing);
-    }
-
-    /**
-     * After creation we will validate that {@link PipelineOptions} conforms to all the
-     * validation criteria from {@code <T>}. See
-     * {@link PipelineOptionsValidator#validate(Class, PipelineOptions)} for more details about
-     * validation.
-     */
-    public Builder withValidation() {
-      return new Builder(args, true, strictParsing);
-    }
-
-    /**
-     * During parsing of the arguments, we will skip over improperly formatted and unknown
-     * arguments.
-     */
-    public Builder withoutStrictParsing() {
-      return new Builder(args, validation, false);
-    }
-
-    /**
-     * Creates and returns an object that implements {@link PipelineOptions} using the values
-     * configured on this builder during construction.
-     *
-     * @return An object that implements {@link PipelineOptions}.
-     */
-    public PipelineOptions create() {
-      return as(PipelineOptions.class);
-    }
-
-    /**
-     * Creates and returns an object that implements {@code <T>} using the values configured on
-     * this builder during construction.
-     *
-     * <p>Note that {@code <T>} must be composable with every registered interface with this
-     * factory. See {@link PipelineOptionsFactory#validateWellFormed(Class, Set)} for more
-     * details.
-     *
-     * @return An object that implements {@code <T>}.
-     */
-    public <T extends PipelineOptions> T as(Class<T> klass) {
-      Map<String, Object> initialOptions = Maps.newHashMap();
-
-      // Attempt to parse the arguments into the set of initial options to use
-      if (args != null) {
-        ListMultimap<String, String> options = parseCommandLine(args, strictParsing);
-        LOG.debug("Provided Arguments: {}", options);
-        printHelpUsageAndExitIfNeeded(options, System.out, true /* exit */);
-        initialOptions = parseObjects(klass, options, strictParsing);
-      }
-
-      // Create our proxy
-      ProxyInvocationHandler handler = new ProxyInvocationHandler(initialOptions);
-      T t = handler.as(klass);
-
-      // Set the application name to the default if none was set.
-      ApplicationNameOptions appNameOptions = t.as(ApplicationNameOptions.class);
-      if (appNameOptions.getAppName() == null) {
-        appNameOptions.setAppName(defaultAppName);
-      }
-
-      if (validation) {
-        PipelineOptionsValidator.validate(klass, t);
-      }
-      return t;
-    }
-  }
-
-  /**
-   * Determines whether the generic {@code --help} was requested or help was
-   * requested for a specific class and invokes the appropriate
-   * {@link PipelineOptionsFactory#printHelp(PrintStream)} and
-   * {@link PipelineOptionsFactory#printHelp(PrintStream, Class)} variant.
-   * Prints to the specified {@link PrintStream}, and exits if requested.
-   *
-   * <p>Visible for testing.
-   * {@code printStream} and {@code exit} used for testing.
-   */
-  @SuppressWarnings("unchecked")
-  static boolean printHelpUsageAndExitIfNeeded(ListMultimap<String, String> options,
-      PrintStream printStream, boolean exit) {
-    if (options.containsKey("help")) {
-      final String helpOption = Iterables.getOnlyElement(options.get("help"));
-
-      // Print the generic help if only --help was specified.
-      if (Boolean.TRUE.toString().equals(helpOption)) {
-        printHelp(printStream);
-        if (exit) {
-          System.exit(0);
-        } else {
-          return true;
-        }
-      }
-
-      // Otherwise attempt to print the specific help option.
-      try {
-        Class<?> klass = Class.forName(helpOption);
-        if (!PipelineOptions.class.isAssignableFrom(klass)) {
-          throw new ClassNotFoundException("PipelineOptions of type " + klass + " not found.");
-        }
-        printHelp(printStream, (Class<? extends PipelineOptions>) klass);
-      } catch (ClassNotFoundException e) {
-        // If we didn't find an exact match, look for any that match the class name.
-        Iterable<Class<? extends PipelineOptions>> matches = Iterables.filter(
-            getRegisteredOptions(),
-            new Predicate<Class<? extends PipelineOptions>>() {
-              @Override
-              public boolean apply(Class<? extends PipelineOptions> input) {
-                if (helpOption.contains(".")) {
-                  return input.getName().endsWith(helpOption);
-                } else {
-                  return input.getSimpleName().equals(helpOption);
-                }
-              }
-          });
-        try {
-          printHelp(printStream, Iterables.getOnlyElement(matches));
-        } catch (NoSuchElementException exception) {
-          printStream.format("Unable to find option %s.%n", helpOption);
-          printHelp(printStream);
-        } catch (IllegalArgumentException exception) {
-          printStream.format("Multiple matches found for %s: %s.%n", helpOption,
-              Iterables.transform(matches, ReflectHelpers.CLASS_NAME));
-          printHelp(printStream);
-        }
-      }
-      if (exit) {
-        System.exit(0);
-      } else {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Returns the simple name of the calling class using the current threads stack.
-   */
-  private static String findCallersClassName() {
-    Iterator<StackTraceElement> elements =
-        Iterators.forArray(Thread.currentThread().getStackTrace());
-    // First find the PipelineOptionsFactory/Builder class in the stack trace.
-    while (elements.hasNext()) {
-      StackTraceElement next = elements.next();
-      if (PIPELINE_OPTIONS_FACTORY_CLASSES.contains(next.getClassName())) {
-        break;
-      }
-    }
-    // Then find the first instance after that is not the PipelineOptionsFactory/Builder class.
-    while (elements.hasNext()) {
-      StackTraceElement next = elements.next();
-      if (!PIPELINE_OPTIONS_FACTORY_CLASSES.contains(next.getClassName())) {
-        try {
-          return Class.forName(next.getClassName()).getSimpleName();
-        } catch (ClassNotFoundException e) {
-          break;
-        }
-      }
-    }
-
-    return "unknown";
-  }
-
-  /**
-   * Stores the generated proxyClass and its respective {@link BeanInfo} object.
-   *
-   * @param <T> The type of the proxyClass.
-   */
-  static class Registration<T extends PipelineOptions> {
-    private final Class<T> proxyClass;
-    private final List<PropertyDescriptor> propertyDescriptors;
-
-    public Registration(Class<T> proxyClass, List<PropertyDescriptor> beanInfo) {
-      this.proxyClass = proxyClass;
-      this.propertyDescriptors = beanInfo;
-    }
-
-    List<PropertyDescriptor> getPropertyDescriptors() {
-      return propertyDescriptors;
-    }
-
-    Class<T> getProxyClass() {
-      return proxyClass;
-    }
-  }
-
-  private static final Set<Class<?>> SIMPLE_TYPES = ImmutableSet.<Class<?>>builder()
-      .add(boolean.class)
-      .add(Boolean.class)
-      .add(char.class)
-      .add(Character.class)
-      .add(short.class)
-      .add(Short.class)
-      .add(int.class)
-      .add(Integer.class)
-      .add(long.class)
-      .add(Long.class)
-      .add(float.class)
-      .add(Float.class)
-      .add(double.class)
-      .add(Double.class)
-      .add(String.class)
-      .add(Class.class).build();
-  private static final Logger LOG = LoggerFactory.getLogger(PipelineOptionsFactory.class);
-  @SuppressWarnings("rawtypes")
-  private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
-  private static final ObjectMapper MAPPER = new ObjectMapper();
-  private static final Map<String, Class<? extends PipelineRunner<?>>> SUPPORTED_PIPELINE_RUNNERS;
-
-  /** Classes that are used as the boundary in the stack trace to find the callers class name. */
-  private static final Set<String> PIPELINE_OPTIONS_FACTORY_CLASSES =
-      ImmutableSet.of(PipelineOptionsFactory.class.getName(), Builder.class.getName());
-
-  /** Methods that are ignored when validating the proxy class. */
-  private static final Set<Method> IGNORED_METHODS;
-
-  /** A predicate that checks if a method is synthetic via {@link Method#isSynthetic()}. */
-  private static final Predicate<Method> NOT_SYNTHETIC_PREDICATE =
-      new Predicate<Method>() {
-        @Override
-        public boolean apply(Method input) {
-          return !input.isSynthetic();
-        }
-      };
-
-  /** The set of options that have been registered and visible to the user. */
-  private static final Set<Class<? extends PipelineOptions>> REGISTERED_OPTIONS =
-      Sets.newConcurrentHashSet();
-
-  /** A cache storing a mapping from a given interface to its registration record. */
-  private static final Map<Class<? extends PipelineOptions>, Registration<?>> INTERFACE_CACHE =
-      Maps.newConcurrentMap();
-
-  /** A cache storing a mapping from a set of interfaces to its registration record. */
-  private static final Map<Set<Class<? extends PipelineOptions>>, Registration<?>> COMBINED_CACHE =
-      Maps.newConcurrentMap();
-
-  /** The width at which options should be output. */
-  private static final int TERMINAL_WIDTH = 80;
-
-  /**
-   * Finds the appropriate {@code ClassLoader} to be used by the
-   * {@link ServiceLoader#load} call, which by default would use the context
-   * {@code ClassLoader}, which can be null. The fallback is as follows: context
-   * ClassLoader, class ClassLoader and finaly the system ClassLoader.
-   */
-  static ClassLoader findClassLoader() {
-    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-    if (classLoader == null) {
-      classLoader = PipelineOptionsFactory.class.getClassLoader();
-    }
-    if (classLoader == null) {
-      classLoader = ClassLoader.getSystemClassLoader();
-    }
-    return classLoader;
-  }
-
-  static {
-    try {
-      IGNORED_METHODS = ImmutableSet.<Method>builder()
-          .add(Object.class.getMethod("getClass"))
-          .add(Object.class.getMethod("wait"))
-          .add(Object.class.getMethod("wait", long.class))
-          .add(Object.class.getMethod("wait", long.class, int.class))
-          .add(Object.class.getMethod("notify"))
-          .add(Object.class.getMethod("notifyAll"))
-          .add(Proxy.class.getMethod("getInvocationHandler", Object.class))
-          .build();
-    } catch (NoSuchMethodException | SecurityException e) {
-      LOG.error("Unable to find expected method", e);
-      throw new ExceptionInInitializerError(e);
-    }
-
-    ClassLoader classLoader = findClassLoader();
-
-    // Store the list of all available pipeline runners.
-    ImmutableMap.Builder<String, Class<? extends PipelineRunner<?>>> builder =
-            ImmutableMap.builder();
-    Set<PipelineRunnerRegistrar> pipelineRunnerRegistrars =
-        Sets.newTreeSet(ObjectsClassComparator.INSTANCE);
-    pipelineRunnerRegistrars.addAll(
-        Lists.newArrayList(ServiceLoader.load(PipelineRunnerRegistrar.class, classLoader)));
-    for (PipelineRunnerRegistrar registrar : pipelineRunnerRegistrars) {
-      for (Class<? extends PipelineRunner<?>> klass : registrar.getPipelineRunners()) {
-        builder.put(klass.getSimpleName(), klass);
-      }
-    }
-    SUPPORTED_PIPELINE_RUNNERS = builder.build();
-
-    // Load and register the list of all classes that extend PipelineOptions.
-    register(PipelineOptions.class);
-    Set<PipelineOptionsRegistrar> pipelineOptionsRegistrars =
-        Sets.newTreeSet(ObjectsClassComparator.INSTANCE);
-    pipelineOptionsRegistrars.addAll(
-        Lists.newArrayList(ServiceLoader.load(PipelineOptionsRegistrar.class, classLoader)));
-    for (PipelineOptionsRegistrar registrar : pipelineOptionsRegistrars) {
-      for (Class<? extends PipelineOptions> klass : registrar.getPipelineOptions()) {
-        register(klass);
-      }
-    }
-  }
-
-  /**
-   * This registers the interface with this factory. This interface must conform to the following
-   * restrictions:
-   * <ul>
-   *   <li>Any property with the same name must have the same return type for all derived
-   *       interfaces of {@link PipelineOptions}.
-   *   <li>Every bean property of any interface derived from {@link PipelineOptions} must have a
-   *       getter and setter method.
-   *   <li>Every method must conform to being a getter or setter for a JavaBean.
-   *   <li>The derived interface of {@link PipelineOptions} must be composable with every interface
-   *       registered with this factory.
-   * </ul>
-   *
-   * @param iface The interface object to manually register.
-   */
-  public static synchronized void register(Class<? extends PipelineOptions> iface) {
-    Preconditions.checkNotNull(iface);
-    Preconditions.checkArgument(iface.isInterface(), "Only interface types are supported.");
-
-    if (REGISTERED_OPTIONS.contains(iface)) {
-      return;
-    }
-    validateWellFormed(iface, REGISTERED_OPTIONS);
-    REGISTERED_OPTIONS.add(iface);
-  }
-
-  /**
-   * Validates that the interface conforms to the following:
-   * <ul>
-   *   <li>Any property with the same name must have the same return type for all derived
-   *       interfaces of {@link PipelineOptions}.
-   *   <li>Every bean property of any interface derived from {@link PipelineOptions} must have a
-   *       getter and setter method.
-   *   <li>Every method must conform to being a getter or setter for a JavaBean.
-   *   <li>The derived interface of {@link PipelineOptions} must be composable with every interface
-   *       part of allPipelineOptionsClasses.
-   *   <li>Only getters may be annotated with {@link JsonIgnore @JsonIgnore}.
-   *   <li>If any getter is annotated with {@link JsonIgnore @JsonIgnore}, then all getters for
-   *       this property must be annotated with {@link JsonIgnore @JsonIgnore}.
-   * </ul>
-   *
-   * @param iface The interface to validate.
-   * @param validatedPipelineOptionsInterfaces The set of validated pipeline options interfaces to
-   *        validate against.
-   * @return A registration record containing the proxy class and bean info for iface.
-   */
-  static synchronized <T extends PipelineOptions> Registration<T> validateWellFormed(
-      Class<T> iface, Set<Class<? extends PipelineOptions>> validatedPipelineOptionsInterfaces) {
-    Preconditions.checkArgument(iface.isInterface(), "Only interface types are supported.");
-
-    @SuppressWarnings("unchecked")
-    Set<Class<? extends PipelineOptions>> combinedPipelineOptionsInterfaces =
-        FluentIterable.from(validatedPipelineOptionsInterfaces).append(iface).toSet();
-    // Validate that the view of all currently passed in options classes is well formed.
-    if (!COMBINED_CACHE.containsKey(combinedPipelineOptionsInterfaces)) {
-      @SuppressWarnings("unchecked")
-      Class<T> allProxyClass =
-          (Class<T>) Proxy.getProxyClass(PipelineOptionsFactory.class.getClassLoader(),
-              combinedPipelineOptionsInterfaces.toArray(EMPTY_CLASS_ARRAY));
-      try {
-        List<PropertyDescriptor> propertyDescriptors =
-            validateClass(iface, validatedPipelineOptionsInterfaces, allProxyClass);
-        COMBINED_CACHE.put(combinedPipelineOptionsInterfaces,
-            new Registration<T>(allProxyClass, propertyDescriptors));
-      } catch (IntrospectionException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    // Validate that the local view of the class is well formed.
-    if (!INTERFACE_CACHE.containsKey(iface)) {
-      @SuppressWarnings({"rawtypes", "unchecked"})
-      Class<T> proxyClass = (Class<T>) Proxy.getProxyClass(
-          PipelineOptionsFactory.class.getClassLoader(), new Class[] {iface});
-      try {
-        List<PropertyDescriptor> propertyDescriptors =
-            validateClass(iface, validatedPipelineOptionsInterfaces, proxyClass);
-        INTERFACE_CACHE.put(iface,
-            new Registration<T>(proxyClass, propertyDescriptors));
-      } catch (IntrospectionException e) {
-        throw new RuntimeException(e);
-      }
-    }
-    @SuppressWarnings("unchecked")
-    Registration<T> result = (Registration<T>) INTERFACE_CACHE.get(iface);
-    return result;
-  }
-
-  public static Set<Class<? extends PipelineOptions>> getRegisteredOptions() {
-    return Collections.unmodifiableSet(REGISTERED_OPTIONS);
-  }
-
-  /**
-   * Outputs the set of registered options with the PipelineOptionsFactory
-   * with a description for each one if available to the output stream. This output
-   * is pretty printed and meant to be human readable. This method will attempt to
-   * format its output to be compatible with a terminal window.
-   */
-  public static void printHelp(PrintStream out) {
-    Preconditions.checkNotNull(out);
-    out.println("The set of registered options are:");
-    Set<Class<? extends PipelineOptions>> sortedOptions =
-        new TreeSet<>(ClassNameComparator.INSTANCE);
-    sortedOptions.addAll(REGISTERED_OPTIONS);
-    for (Class<? extends PipelineOptions> kls : sortedOptions) {
-      out.format("  %s%n", kls.getName());
-    }
-    out.format("%nUse --help=<OptionsName> for detailed help. For example:%n"
-        + "  --help=DataflowPipelineOptions <short names valid for registered options>%n"
-        + "  --help=com.google.cloud.dataflow.sdk.options.DataflowPipelineOptions%n");
-  }
-
-  /**
-   * Outputs the set of options available to be set for the passed in {@link PipelineOptions}
-   * interface. The output is in a human readable format. The format is:
-   * <pre>
-   * OptionGroup:
-   *     ... option group description ...
-   *
-   *  --option1={@code <type>} or list of valid enum choices
-   *     Default: value (if available, see {@link Default})
-   *     ... option description ... (if available, see {@link Description})
-   *     Required groups (if available, see {@link Required})
-   *  --option2={@code <type>} or list of valid enum choices
-   *     Default: value (if available, see {@link Default})
-   *     ... option description ... (if available, see {@link Description})
-   *     Required groups (if available, see {@link Required})
-   * </pre>
-   * This method will attempt to format its output to be compatible with a terminal window.
-   */
-  public static void printHelp(PrintStream out, Class<? extends PipelineOptions> iface) {
-    Preconditions.checkNotNull(out);
-    Preconditions.checkNotNull(iface);
-    validateWellFormed(iface, REGISTERED_OPTIONS);
-
-    Iterable<Method> methods =
-        Iterables.filter(
-            ReflectHelpers.getClosureOfMethodsOnInterface(iface), NOT_SYNTHETIC_PREDICATE);
-    ListMultimap<Class<?>, Method> ifaceToMethods = ArrayListMultimap.create();
-    for (Method method : methods) {
-      // Process only methods that are not marked as hidden.
-      if (method.getAnnotation(Hidden.class) == null) {
-        ifaceToMethods.put(method.getDeclaringClass(), method);
-      }
-    }
-    SortedSet<Class<?>> ifaces = new TreeSet<>(ClassNameComparator.INSTANCE);
-    // Keep interfaces that are not marked as hidden.
-    ifaces.addAll(Collections2.filter(ifaceToMethods.keySet(), new Predicate<Class<?>>() {
-      @Override
-      public boolean apply(Class<?> input) {
-        return input.getAnnotation(Hidden.class) == null;
-      }
-    }));
-    for (Class<?> currentIface : ifaces) {
-      Map<String, Method> propertyNamesToGetters =
-          getPropertyNamesToGetters(ifaceToMethods.get(currentIface));
-
-      // Don't output anything if there are no defined options
-      if (propertyNamesToGetters.isEmpty()) {
-        continue;
-      }
-      SortedSetMultimap<String, String> requiredGroupNameToProperties =
-          getRequiredGroupNamesToProperties(propertyNamesToGetters);
-
-      out.format("%s:%n", currentIface.getName());
-      prettyPrintDescription(out, currentIface.getAnnotation(Description.class));
-
-      out.println();
-
-      List<String> lists = Lists.newArrayList(propertyNamesToGetters.keySet());
-      Collections.sort(lists, String.CASE_INSENSITIVE_ORDER);
-      for (String propertyName : lists) {
-        Method method = propertyNamesToGetters.get(propertyName);
-        String printableType = method.getReturnType().getSimpleName();
-        if (method.getReturnType().isEnum()) {
-          printableType = Joiner.on(" | ").join(method.getReturnType().getEnumConstants());
-        }
-        out.format("  --%s=<%s>%n", propertyName, printableType);
-        Optional<String> defaultValue = getDefaultValueFromAnnotation(method);
-        if (defaultValue.isPresent()) {
-          out.format("    Default: %s%n", defaultValue.get());
-        }
-        prettyPrintDescription(out, method.getAnnotation(Description.class));
-        prettyPrintRequiredGroups(out, method.getAnnotation(Validation.Required.class),
-            requiredGroupNameToProperties);
-      }
-      out.println();
-    }
-  }
-
-  /**
-   * Output the requirement groups that the property is a member of, including all properties that
-   * satisfy the group requirement, breaking up long lines on white space characters and attempting
-   * to honor a line limit of {@code TERMINAL_WIDTH}.
-   */
-  private static void prettyPrintRequiredGroups(PrintStream out, Required annotation,
-      SortedSetMultimap<String, String> requiredGroupNameToProperties) {
-    if (annotation == null || annotation.groups() == null) {
-      return;
-    }
-    for (String group : annotation.groups()) {
-      SortedSet<String> groupMembers = requiredGroupNameToProperties.get(group);
-      String requirement;
-      if (groupMembers.size() == 1) {
-        requirement = Iterables.getOnlyElement(groupMembers) + " is required.";
-      } else {
-        requirement = "At least one of " + groupMembers + " is required";
-      }
-      terminalPrettyPrint(out, requirement.split("\\s+"));
-    }
-  }
-
-  /**
-   * Outputs the value of the description, breaking up long lines on white space characters and
-   * attempting to honor a line limit of {@code TERMINAL_WIDTH}.
-   */
-  private static void prettyPrintDescription(PrintStream out, Description description) {
-    if (description == null || description.value() == null) {
-      return;
-    }
-
-    String[] words = description.value().split("\\s+");
-    terminalPrettyPrint(out, words);
-  }
-
-  private static void terminalPrettyPrint(PrintStream out, String[] words) {
-    final String spacing = "   ";
-
-    if (words.length == 0) {
-      return;
-    }
-
-    out.print(spacing);
-    int lineLength = spacing.length();
-    for (int i = 0; i < words.length; ++i) {
-      out.print(" ");
-      out.print(words[i]);
-      lineLength += 1 + words[i].length();
-
-      // If the next word takes us over the terminal width, then goto the next line.
-      if (i + 1 != words.length && words[i + 1].length() + lineLength + 1 > TERMINAL_WIDTH) {
-        out.println();
-        out.print(spacing);
-        lineLength = spacing.length();
-      }
-    }
-    out.println();
-  }
-
-  /**
-   * Returns a string representation of the {@link Default} value on the passed in method.
-   */
-  private static Optional<String> getDefaultValueFromAnnotation(Method method) {
-    for (Annotation annotation : method.getAnnotations()) {
-      if (annotation instanceof Default.Class) {
-        return Optional.of(((Default.Class) annotation).value().getSimpleName());
-      } else if (annotation instanceof Default.String) {
-        return Optional.of(((Default.String) annotation).value());
-      } else if (annotation instanceof Default.Boolean) {
-        return Optional.of(Boolean.toString(((Default.Boolean) annotation).value()));
-      } else if (annotation instanceof Default.Character) {
-        return Optional.of(Character.toString(((Default.Character) annotation).value()));
-      } else if (annotation instanceof Default.Byte) {
-        return Optional.of(Byte.toString(((Default.Byte) annotation).value()));
-      } else if (annotation instanceof Default.Short) {
-        return Optional.of(Short.toString(((Default.Short) annotation).value()));
-      } else if (annotation instanceof Default.Integer) {
-        return Optional.of(Integer.toString(((Default.Integer) annotation).value()));
-      } else if (annotation instanceof Default.Long) {
-        return Optional.of(Long.toString(((Default.Long) annotation).value()));
-      } else if (annotation instanceof Default.Float) {
-        return Optional.of(Float.toString(((Default.Float) annotation).value()));
-      } else if (annotation instanceof Default.Double) {
-        return Optional.of(Double.toString(((Default.Double) annotation).value()));
-      } else if (annotation instanceof Default.Enum) {
-        return Optional.of(((Default.Enum) annotation).value());
-      } else if (annotation instanceof Default.InstanceFactory) {
-        return Optional.of(((Default.InstanceFactory) annotation).value().getSimpleName());
-      }
-    }
-    return Optional.absent();
-  }
-
-  static Map<String, Class<? extends PipelineRunner<?>>> getRegisteredRunners() {
-    return SUPPORTED_PIPELINE_RUNNERS;
-  }
-
-  static List<PropertyDescriptor> getPropertyDescriptors(
-      Set<Class<? extends PipelineOptions>> interfaces) {
-    return COMBINED_CACHE.get(interfaces).getPropertyDescriptors();
-  }
-
-  /**
-   * This method is meant to emulate the behavior of {@link Introspector#getBeanInfo(Class, int)}
-   * to construct the list of {@link PropertyDescriptor}.
-   *
-   * <p>TODO: Swap back to using Introspector once the proxy class issue with AppEngine is
-   * resolved.
-   */
-  private static List<PropertyDescriptor> getPropertyDescriptors(Class<?> beanClass)
-      throws IntrospectionException {
-    // The sorting is important to make this method stable.
-    SortedSet<Method> methods = Sets.newTreeSet(MethodComparator.INSTANCE);
-    methods.addAll(
-        Collections2.filter(Arrays.asList(beanClass.getMethods()), NOT_SYNTHETIC_PREDICATE));
-    SortedMap<String, Method> propertyNamesToGetters = getPropertyNamesToGetters(methods);
-    List<PropertyDescriptor> descriptors = Lists.newArrayList();
-
-    List<TypeMismatch> mismatches = new ArrayList<>();
-    /*
-     * Add all the getter/setter pairs to the list of descriptors removing the getter once
-     * it has been paired up.
-     */
-    for (Method method : methods) {
-      String methodName = method.getName();
-      if (!methodName.startsWith("set")
-          || method.getParameterTypes().length != 1
-          || method.getReturnType() != void.class) {
-        continue;
-      }
-      String propertyName = Introspector.decapitalize(methodName.substring(3));
-      Method getterMethod = propertyNamesToGetters.remove(propertyName);
-
-      // Validate that the getter and setter property types are the same.
-      if (getterMethod != null) {
-        Class<?> getterPropertyType = getterMethod.getReturnType();
-        Class<?> setterPropertyType = method.getParameterTypes()[0];
-        if (getterPropertyType != setterPropertyType) {
-          TypeMismatch mismatch = new TypeMismatch();
-          mismatch.propertyName = propertyName;
-          mismatch.getterPropertyType = getterPropertyType;
-          mismatch.setterPropertyType = setterPropertyType;
-          mismatches.add(mismatch);
-          continue;
-        }
-      }
-
-      descriptors.add(new PropertyDescriptor(
-          propertyName, getterMethod, method));
-    }
-    throwForTypeMismatches(mismatches);
-
-    // Add the remaining getters with missing setters.
-    for (Map.Entry<String, Method> getterToMethod : propertyNamesToGetters.entrySet()) {
-      descriptors.add(new PropertyDescriptor(
-          getterToMethod.getKey(), getterToMethod.getValue(), null));
-    }
-    return descriptors;
-  }
-
-  private static class TypeMismatch {
-    private String propertyName;
-    private Class<?> getterPropertyType;
-    private Class<?> setterPropertyType;
-  }
-
-  private static void throwForTypeMismatches(List<TypeMismatch> mismatches) {
-    if (mismatches.size() == 1) {
-      TypeMismatch mismatch = mismatches.get(0);
-      throw new IllegalArgumentException(String.format(
-          "Type mismatch between getter and setter methods for property [%s]. "
-          + "Getter is of type [%s] whereas setter is of type [%s].",
-          mismatch.propertyName,
-          mismatch.getterPropertyType.getName(),
-          mismatch.setterPropertyType.getName()));
-    } else if (mismatches.size() > 1) {
-      StringBuilder builder = new StringBuilder(
-          String.format("Type mismatches between getters and setters detected:"));
-      for (TypeMismatch mismatch : mismatches) {
-        builder.append(String.format(
-            "%n  - Property [%s]: Getter is of type [%s] whereas setter is of type [%s].",
-            mismatch.propertyName,
-            mismatch.getterPropertyType.getName(),
-            mismatch.setterPropertyType.getName()));
-      }
-      throw new IllegalArgumentException(builder.toString());
-    }
-  }
-
-  /**
-   * Returns a map of the property name to the getter method it represents.
-   * If there are duplicate methods with the same bean name, then it is indeterminate
-   * as to which method will be returned.
-   */
-  private static SortedMap<String, Method> getPropertyNamesToGetters(Iterable<Method> methods) {
-    SortedMap<String, Method> propertyNamesToGetters = Maps.newTreeMap();
-    for (Method method : methods) {
-      String methodName = method.getName();
-      if ((!methodName.startsWith("get")
-          && !methodName.startsWith("is"))
-          || method.getParameterTypes().length != 0
-          || method.getReturnType() == void.class) {
-        continue;
-      }
-      String propertyName = Introspector.decapitalize(
-          methodName.startsWith("is") ? methodName.substring(2) : methodName.substring(3));
-      propertyNamesToGetters.put(propertyName, method);
-    }
-    return propertyNamesToGetters;
-  }
-
-  /**
-   * Returns a map of required groups of arguments to the properties that satisfy the requirement.
-   */
-  private static SortedSetMultimap<String, String> getRequiredGroupNamesToProperties(
-      Map<String, Method> propertyNamesToGetters) {
-    SortedSetMultimap<String, String> result = TreeMultimap.create();
-    for (Map.Entry<String, Method> propertyEntry : propertyNamesToGetters.entrySet()) {
-      Required requiredAnnotation =
-          propertyEntry.getValue().getAnnotation(Validation.Required.class);
-      if (requiredAnnotation != null) {
-        for (String groupName : requiredAnnotation.groups()) {
-          result.put(groupName, propertyEntry.getKey());
-        }
-      }
-    }
-    return result;
-  }
-
-  /**
-   * Validates that a given class conforms to the following properties:
-   * <ul>
-   *   <li>Any property with the same name must have the same return type for all derived
-   *       interfaces of {@link PipelineOptions}.
-   *   <li>Every bean property of any interface derived from {@link PipelineOptions} must have a
-   *       getter and setter method.
-   *   <li>Every method must conform to being a getter or setter for a JavaBean.
-   *   <li>Only getters may be annotated with {@link JsonIgnore @JsonIgnore}.
-   *   <li>If any getter is annotated with {@link JsonIgnore @JsonIgnore}, then all getters for
-   *       this property must be annotated with {@link JsonIgnore @JsonIgnore}.
-   * </ul>
-   *
-   * @param iface The interface to validate.
-   * @param validatedPipelineOptionsInterfaces The set of validated pipeline options interfaces to
-   *        validate against.
-   * @param klass The proxy class representing the interface.
-   * @return A list of {@link PropertyDescriptor}s representing all valid bean properties of
-   *         {@code iface}.
-   * @throws IntrospectionException if invalid property descriptors.
-   */
-  private static List<PropertyDescriptor> validateClass(Class<? extends PipelineOptions> iface,
-      Set<Class<? extends PipelineOptions>> validatedPipelineOptionsInterfaces,
-      Class<?> klass) throws IntrospectionException {
-    Set<Method> methods = Sets.newHashSet(IGNORED_METHODS);
-    // Ignore static methods, "equals", "hashCode", "toString" and "as" on the generated class.
-    // Ignore synthetic methods
-    for (Method method : klass.getMethods()) {
-      if (Modifier.isStatic(method.getModifiers()) || method.isSynthetic()) {
-        methods.add(method);
-      }
-    }
-    try {
-      methods.add(klass.getMethod("equals", Object.class));
-      methods.add(klass.getMethod("hashCode"));
-      methods.add(klass.getMethod("toString"));
-      methods.add(klass.getMethod("as", Class.class));
-      methods.add(klass.getMethod("cloneAs", Class.class));
-    } catch (NoSuchMethodException | SecurityException e) {
-      throw Throwables.propagate(e);
-    }
-
-    // Verify that there are no methods with the same name with two different return types.
-    Iterable<Method> interfaceMethods = FluentIterable
-        .from(ReflectHelpers.getClosureOfMethodsOnInterface(iface))
-        .filter(NOT_SYNTHETIC_PREDICATE)
-        .toSortedSet(MethodComparator.INSTANCE);
-    SortedSetMultimap<Method, Method> methodNameToMethodMap =
-        TreeMultimap.create(MethodNameComparator.INSTANCE, MethodComparator.INSTANCE);
-    for (Method method : interfaceMethods) {
-      methodNameToMethodMap.put(method, method);
-    }
-    List<MultipleDefinitions> multipleDefinitions = Lists.newArrayList();
-    for (Map.Entry<Method, Collection<Method>> entry
-        : methodNameToMethodMap.asMap().entrySet()) {
-      Set<Class<?>> returnTypes = FluentIterable.from(entry.getValue())
-          .transform(ReturnTypeFetchingFunction.INSTANCE).toSet();
-      SortedSet<Method> collidingMethods = FluentIterable.from(entry.getValue())
-          .toSortedSet(MethodComparator.INSTANCE);
-      if (returnTypes.size() > 1) {
-        MultipleDefinitions defs = new MultipleDefinitions();
-        defs.method = entry.getKey();
-        defs.collidingMethods = collidingMethods;
-        multipleDefinitions.add(defs);
-      }
-    }
-    throwForMultipleDefinitions(iface, multipleDefinitions);
-
-    // Verify that there is no getter with a mixed @JsonIgnore annotation and verify
-    // that no setter has @JsonIgnore.
-    Iterable<Method> allInterfaceMethods =
-        FluentIterable.from(
-                ReflectHelpers.getClosureOfMethodsOnInterfaces(
-                    validatedPipelineOptionsInterfaces))
-            .append(ReflectHelpers.getClosureOfMethodsOnInterface(iface))
-            .filter(NOT_SYNTHETIC_PREDICATE)
-            .toSortedSet(MethodComparator.INSTANCE);
-    SortedSetMultimap<Method, Method> methodNameToAllMethodMap =
-        TreeMultimap.create(MethodNameComparator.INSTANCE, MethodComparator.INSTANCE);
-    for (Method method : allInterfaceMethods) {
-      methodNameToAllMethodMap.put(method, method);
-    }
-
-    List<PropertyDescriptor> descriptors = getPropertyDescriptors(klass);
-
-    List<InconsistentlyIgnoredGetters> incompletelyIgnoredGetters = new ArrayList<>();
-    List<IgnoredSetter> ignoredSetters = new ArrayList<>();
-
-    for (PropertyDescriptor descriptor : descriptors) {
-      if (descriptor.getReadMethod() == null
-          || descriptor.getWriteMethod() == null
-          || IGNORED_METHODS.contains(descriptor.getReadMethod())
-          || IGNORED_METHODS.contains(descriptor.getWriteMethod())) {
-        continue;
-      }
-      SortedSet<Method> getters = methodNameToAllMethodMap.get(descriptor.getReadMethod());
-      SortedSet<Method> gettersWithJsonIgnore = Sets.filter(getters, JsonIgnorePredicate.INSTANCE);
-
-      Iterable<String> getterClassNames = FluentIterable.from(getters)
-          .transform(MethodToDeclaringClassFunction.INSTANCE)
-          .transform(ReflectHelpers.CLASS_NAME);
-      Iterable<String> gettersWithJsonIgnoreClassNames = FluentIterable.from(gettersWithJsonIgnore)
-          .transform(MethodToDeclaringClassFunction.INSTANCE)
-          .transform(ReflectHelpers.CLASS_NAME);
-
-      if (!(gettersWithJsonIgnore.isEmpty() || getters.size() == gettersWithJsonIgnore.size())) {
-        InconsistentlyIgnoredGetters err = new InconsistentlyIgnoredGetters();
-        err.descriptor = descriptor;
-        err.getterClassNames = getterClassNames;
-        err.gettersWithJsonIgnoreClassNames = gettersWithJsonIgnoreClassNames;
-        incompletelyIgnoredGetters.add(err);
-      }
-      if (!incompletelyIgnoredGetters.isEmpty()) {
-        continue;
-      }
-
-      SortedSet<Method> settersWithJsonIgnore =
-          Sets.filter(methodNameToAllMethodMap.get(descriptor.getWriteMethod()),
-              JsonIgnorePredicate.INSTANCE);
-
-      Iterable<String> settersWithJsonIgnoreClassNames = FluentIterable.from(settersWithJsonIgnore)
-              .transform(MethodToDeclaringClassFunction.INSTANCE)
-              .transform(ReflectHelpers.CLASS_NAME);
-
-      if (!settersWithJsonIgnore.isEmpty()) {
-        IgnoredSetter ignored = new IgnoredSetter();
-        ignored.descriptor = descriptor;
-        ignored.settersWithJsonIgnoreClassNames = settersWithJsonIgnoreClassNames;
-        ignoredSetters.add(ignored);
-      }
-    }
-    throwForGettersWithInconsistentJsonIgnore(incompletelyIgnoredGetters);
-    throwForSettersWithJsonIgnore(ignoredSetters);
-
-    List<MissingBeanMethod> missingBeanMethods = new ArrayList<>();
-    // Verify that each property has a matching read and write method.
-    for (PropertyDescriptor propertyDescriptor : descriptors) {
-      if (!(IGNORED_METHODS.contains(propertyDescriptor.getWriteMethod())
-        || propertyDescriptor.getReadMethod() != null)) {
-        MissingBeanMethod method = new MissingBeanMethod();
-        method.property = propertyDescriptor;
-        method.methodType = "getter";
-        missingBeanMethods.add(method);
-        continue;
-      }
-      if (!(IGNORED_METHODS.contains(propertyDescriptor.getReadMethod())
-              || propertyDescriptor.getWriteMethod() != null)) {
-        MissingBeanMethod method = new MissingBeanMethod();
-        method.property = propertyDescriptor;
-        method.methodType = "setter";
-        missingBeanMethods.add(method);
-        continue;
-      }
-      methods.add(propertyDescriptor.getReadMethod());
-      methods.add(propertyDescriptor.getWriteMethod());
-    }
-    throwForMissingBeanMethod(iface, missingBeanMethods);
-
-    // Verify that no additional methods are on an interface that aren't a bean property.
-    SortedSet<Method> unknownMethods = new TreeSet<>(MethodComparator.INSTANCE);
-    unknownMethods.addAll(
-        Sets.filter(
-            Sets.difference(Sets.newHashSet(klass.getMethods()), methods),
-            NOT_SYNTHETIC_PREDICATE));
-    Preconditions.checkArgument(unknownMethods.isEmpty(),
-        "Methods %s on [%s] do not conform to being bean properties.",
-        FluentIterable.from(unknownMethods).transform(ReflectHelpers.METHOD_FORMATTER),
-        iface.getName());
-
-    return descriptors;
-  }
-
-  private static class MultipleDefinitions {
-    private Method method;
-    private SortedSet<Method> collidingMethods;
-  }
-
-  private static void throwForMultipleDefinitions(
-      Class<? extends PipelineOptions> iface, List<MultipleDefinitions> definitions) {
-    if (definitions.size() == 1) {
-      MultipleDefinitions errDef = definitions.get(0);
-      throw new IllegalArgumentException(String.format(
-          "Method [%s] has multiple definitions %s with different return types for [%s].",
-          errDef.method.getName(), errDef.collidingMethods, iface.getName()));
-    } else if (definitions.size() > 1) {
-      StringBuilder errorBuilder = new StringBuilder(String.format(
-          "Interface [%s] has Methods with multiple definitions with different return types:",
-          iface.getName()));
-      for (MultipleDefinitions errDef : definitions) {
-        errorBuilder.append(String.format(
-            "%n  - Method [%s] has multiple definitions %s",
-            errDef.method.getName(),
-            errDef.collidingMethods));
-      }
-      throw new IllegalArgumentException(errorBuilder.toString());
-    }
-  }
-
-  private static class InconsistentlyIgnoredGetters {
-    PropertyDescriptor descriptor;
-    Iterable<String> getterClassNames;
-    Iterable<String> gettersWithJsonIgnoreClassNames;
-  }
-
-  private static void throwForGettersWithInconsistentJsonIgnore(
-      List<InconsistentlyIgnoredGetters> getters) {
-    if (getters.size() == 1) {
-      InconsistentlyIgnoredGetters getter = getters.get(0);
-      throw new IllegalArgumentException(String.format(
-          "Expected getter for property [%s] to be marked with @JsonIgnore on all %s, "
-          + "found only on %s",
-          getter.descriptor.getName(), getter.getterClassNames,
-          getter.gettersWithJsonIgnoreClassNames));
-    } else if (getters.size() > 1) {
-      StringBuilder errorBuilder =
-          new StringBuilder("Property getters are inconsistently marked with @JsonIgnore:");
-      for (InconsistentlyIgnoredGetters getter : getters) {
-        errorBuilder.append(
-            String.format("%n  - Expected for property [%s] to be marked on all %s, "
-                + "found only on %s",
-                getter.descriptor.getName(), getter.getterClassNames,
-                getter.gettersWithJsonIgnoreClassNames));
-      }
-      throw new IllegalArgumentException(errorBuilder.toString());
-    }
-  }
-
-  private static class IgnoredSetter {
-    PropertyDescriptor descriptor;
-    Iterable<String> settersWithJsonIgnoreClassNames;
-  }
-
-  private static void throwForSettersWithJsonIgnore(List<IgnoredSetter> setters) {
-    if (setters.size() == 1) {
-      IgnoredSetter setter = setters.get(0);
-      throw new IllegalArgumentException(
-          String.format("Expected setter for property [%s] to not be marked with @JsonIgnore on %s",
-              setter.descriptor.getName(), setter.settersWithJsonIgnoreClassNames));
-    } else if (setters.size() > 1) {
-      StringBuilder builder = new StringBuilder("Found setters marked with @JsonIgnore:");
-      for (IgnoredSetter setter : setters) {
-        builder.append(
-            String.format("%n  - Setter for property [%s] should not be marked with @JsonIgnore "
-                + "on %s",
-                setter.descriptor.getName(), setter.settersWithJsonIgnoreClassNames));
-      }
-      throw new IllegalArgumentException(builder.toString());
-    }
-  }
-
-  private static class MissingBeanMethod {
-    String methodType;
-    PropertyDescriptor property;
-  }
-
-  private static void throwForMissingBeanMethod(
-      Class<? extends PipelineOptions> iface, List<MissingBeanMethod> missingBeanMethods) {
-    if (missingBeanMethods.size() == 1) {
-      MissingBeanMethod missingBeanMethod = missingBeanMethods.get(0);
-      throw new IllegalArgumentException(
-          String.format("Expected %s for property [%s] of type [%s] on [%s].",
-              missingBeanMethod.methodType, missingBeanMethod.property.getName(),
-              missingBeanMethod.property.getPropertyType().getName(), iface.getName()));
-    } else if (missingBeanMethods.size() > 1) {
-      StringBuilder builder = new StringBuilder(String.format(
-          "Found missing property methods on [%s]:", iface.getName()));
-      for (MissingBeanMethod method : missingBeanMethods) {
-        builder.append(
-            String.format("%n  - Expected %s for property [%s] of type [%s]", method.methodType,
-                method.property.getName(), method.property.getPropertyType().getName()));
-      }
-      throw new IllegalArgumentException(builder.toString());
-    }
-  }
-
-  /** A {@link Comparator} that uses the classes name to compare them. */
-  private static class ClassNameComparator implements Comparator<Class<?>> {
-    static final ClassNameComparator INSTANCE = new ClassNameComparator();
-    @Override
-    public int compare(Class<?> o1, Class<?> o2) {
-      return o1.getName().compareTo(o2.getName());
-    }
-  }
-
-  /** A {@link Comparator} that uses the object's classes canonical name to compare them. */
-  private static class ObjectsClassComparator implements Comparator<Object> {
-    static final ObjectsClassComparator INSTANCE = new ObjectsClassComparator();
-    @Override
-    public int compare(Object o1, Object o2) {
-      return o1.getClass().getCanonicalName().compareTo(o2.getClass().getCanonicalName());
-    }
-  }
-
-  /** A {@link Comparator} that uses the generic method signature to sort them. */
-  private static class MethodComparator implements Comparator<Method> {
-    static final MethodComparator INSTANCE = new MethodComparator();
-    @Override
-    public int compare(Method o1, Method o2) {
-      return o1.toGenericString().compareTo(o2.toGenericString());
-    }
-  }
-
-  /** A {@link Comparator} that uses the methods name to compare them. */
-  static class MethodNameComparator implements Comparator<Method> {
-    static final MethodNameComparator INSTANCE = new MethodNameComparator();
-    @Override
-    public int compare(Method o1, Method o2) {
-      return o1.getName().compareTo(o2.getName());
-    }
-  }
-
-  /** A {@link Function} that gets the method's return type. */
-  private static class ReturnTypeFetchingFunction implements Function<Method, Class<?>> {
-    static final ReturnTypeFetchingFunction INSTANCE = new ReturnTypeFetchingFunction();
-    @Override
-    public Class<?> apply(Method input) {
-      return input.getReturnType();
-    }
-  }
-
-  /** A {@link Function} with returns the declaring class for the method. */
-  private static class MethodToDeclaringClassFunction implements Function<Method, Class<?>> {
-    static final MethodToDeclaringClassFunction INSTANCE = new MethodToDeclaringClassFunction();
-    @Override
-    public Class<?> apply(Method input) {
-      return input.getDeclaringClass();
-    }
-  }
-
-  /**
-   * A {@link Predicate} that returns true if the method is annotated with
-   * {@link JsonIgnore @JsonIgnore}.
-   */
-  static class JsonIgnorePredicate implements Predicate<Method> {
-    static final JsonIgnorePredicate INSTANCE = new JsonIgnorePredicate();
-    @Override
-    public boolean apply(Method input) {
-      return input.isAnnotationPresent(JsonIgnore.class);
-    }
-  }
-
-  /**
-   * Splits string arguments based upon expected pattern of --argName=value.
-   *
-   * <p>Example GNU style command line arguments:
-   *
-   * <pre>
-   *   --project=MyProject (simple property, will set the "project" property to "MyProject")
-   *   --readOnly=true (for boolean properties, will set the "readOnly" property to "true")
-   *   --readOnly (shorthand for boolean properties, will set the "readOnly" property to "true")
-   *   --x=1 --x=2 --x=3 (list style simple property, will set the "x" property to [1, 2, 3])
-   *   --x=1,2,3 (shorthand list style simple property, will set the "x" property to [1, 2, 3])
-   *   --complexObject='{"key1":"value1",...} (JSON format for all other complex types)
-   * </pre>
-   *
-   * <p>Simple properties are able to bound to {@link String}, {@link Class}, enums and Java
-   * primitives {@code boolean}, {@code byte}, {@code short}, {@code int}, {@code long},
-   * {@code float}, {@code double} and their primitive wrapper classes.
-   *
-   * <p>Simple list style properties are able to be bound to {@code boolean[]}, {@code char[]},
-   * {@code short[]}, {@code int[]}, {@code long[]}, {@code float[]}, {@code double[]},
-   * {@code Class[]}, enum arrays, {@code String[]}, and {@code List<String>}.
-   *
-   * <p>JSON format is required for all other types.
-   *
-   * <p>If strict parsing is enabled, options must start with '--', and not have an empty argument
-   * name or value based upon the positioning of the '='. Empty or null arguments will be ignored
-   * whether or not strict parsing is enabled.
-   */
-  private static ListMultimap<String, String> parseCommandLine(
-      String[] args, boolean strictParsing) {
-    ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder();
-    for (String arg : args) {
-      if (Strings.isNullOrEmpty(arg)) {
-        continue;
-      }
-      try {
-        Preconditions.checkArgument(arg.startsWith("--"),
-            "Argument '%s' does not begin with '--'", arg);
-        int index = arg.indexOf("=");
-        // Make sure that '=' isn't the first character after '--' or the last character
-        Preconditions.checkArgument(index != 2,
-            "Argument '%s' starts with '--=', empty argument name not allowed", arg);
-        if (index > 0) {
-          builder.put(arg.substring(2, index), arg.substring(index + 1, arg.length()));
-        } else {
-          builder.put(arg.substring(2), "true");
-        }
-      } catch (IllegalArgumentException e) {
-        if (strictParsing) {
-          throw e;
-        } else {
-          LOG.warn("Strict parsing is disabled, ignoring option '{}' because {}",
-              arg, e.getMessage());
-        }
-      }
-    }
-    return builder.build();
-  }
-
-  /**
-   * Using the parsed string arguments, we convert the strings to the expected
-   * return type of the methods that are found on the passed-in class.
-   *
-   * <p>For any return type that is expected to be an array or a collection, we further
-   * split up each string on ','.
-   *
-   * <p>We special case the "runner" option. It is mapped to the class of the {@link PipelineRunner}
-   * based off of the {@link PipelineRunner PipelineRunners} simple class name. If the provided
-   * runner name is not registered via a {@link PipelineRunnerRegistrar}, we attempt to obtain the
-   * class that the name represents using {@link Class#forName(String)} and use the result class if
-   * it subclasses {@link PipelineRunner}.
-   *
-   * <p>If strict parsing is enabled, unknown options or options that cannot be converted to
-   * the expected java type using an {@link ObjectMapper} will be ignored.
-   */
-  private static <T extends PipelineOptions> Map<String, Object> parseObjects(
-      Class<T> klass, ListMultimap<String, String> options, boolean strictParsing) {
-    Map<String, Method> propertyNamesToGetters = Maps.newHashMap();
-    PipelineOptionsFactory.validateWellFormed(klass, REGISTERED_OPTIONS);
-    @SuppressWarnings("unchecked")
-    Iterable<PropertyDescriptor> propertyDescriptors =
-        PipelineOptionsFactory.getPropertyDescriptors(
-            FluentIterable.from(getRegisteredOptions()).append(klass).toSet());
-    for (PropertyDescriptor descriptor : propertyDescriptors) {
-      propertyNamesToGetters.put(descriptor.getName(), descriptor.getReadMethod());
-    }
-    Map<String, Object> convertedOptions = Maps.newHashMap();
-    for (final Map.Entry<String, Collection<String>> entry : options.asMap().entrySet()) {
-      try {
-        // Search for close matches for missing properties.
-        // Either off by one or off by two character errors.
-        if (!propertyNamesToGetters.containsKey(entry.getKey())) {
-          SortedSet<String> closestMatches = new TreeSet<String>(
-              Sets.filter(propertyNamesToGetters.keySet(), new Predicate<String>() {
-                @Override
-                public boolean apply(@Nullable String input) {
-                  return StringUtils.getLevenshteinDistance(entry.getKey(), input) <= 2;
-                }
-          }));
-          switch (closestMatches.size()) {
-            case 0:
-              throw new IllegalArgumentException(
-                  String.format("Class %s missing a property named '%s'.",
-                      klass, entry.getKey()));
-            case 1:
-              throw new IllegalArgumentException(
-                  String.format("Class %s missing a property named '%s'. Did you mean '%s'?",
-                      klass, entry.getKey(), Iterables.getOnlyElement(closestMatches)));
-            default:
-              throw new IllegalArgumentException(
-                  String.format("Class %s missing a property named '%s'. Did you mean one of %s?",
-                      klass, entry.getKey(), closestMatches));
-          }
-        }
-
-        Method method = propertyNamesToGetters.get(entry.getKey());
-        // Only allow empty argument values for String, String Array, and Collection.
-        Class<?> returnType = method.getReturnType();
-        JavaType type = MAPPER.getTypeFactory().constructType(method.getGenericReturnType());
-        if ("runner".equals(entry.getKey())) {
-          String runner = Iterables.getOnlyElement(entry.getValue());
-          if (SUPPORTED_PIPELINE_RUNNERS.containsKey(runner)) {
-            convertedOptions.put("runner", SUPPORTED_PIPELINE_RUNNERS.get(runner));
-          } else {
-            try {
-              Class<?> runnerClass = Class.forName(runner);
-              checkArgument(
-                  PipelineRunner.class.isAssignableFrom(runnerClass),
-                  "Class '%s' does not implement PipelineRunner. Supported pipeline runners %s",
-                  runner,
-                  Sets.newTreeSet(SUPPORTED_PIPELINE_RUNNERS.keySet()));
-              convertedOptions.put("runner", runnerClass);
-            } catch (ClassNotFoundException e) {
-              String msg =
-                  String.format(
-                      "Unknown 'runner' specified '%s', supported pipeline runners %s",
-                      runner,
-                      Sets.newTreeSet(SUPPORTED_PIPELINE_RUNNERS.keySet()));
-                throw new IllegalArgumentException(msg, e);
-            }
-          }
-        } else if ((returnType.isArray() && (SIMPLE_TYPES.contains(returnType.getComponentType())
-                || returnType.getComponentType().isEnum()))
-            || Collection.class.isAssignableFrom(returnType)) {
-          // Split any strings with ","
-          List<String> values = FluentIterable.from(entry.getValue())
-              .transformAndConcat(new Function<String, Iterable<String>>() {
-                @Override
-                public Iterable<String> apply(String input) {
-                  return Arrays.asList(input.split(","));
-                }
-          }).toList();
-
-          if (returnType.isArray() && !returnType.getComponentType().equals(String.class)) {
-            for (String value : values) {
-              Preconditions.checkArgument(!value.isEmpty(),
-                  "Empty argument value is only allowed for String, String Array, and Collection,"
-                  + " but received: " + returnType);
-            }
-          }
-          convertedOptions.put(entry.getKey(), MAPPER.convertValue(values, type));
-        } else if (SIMPLE_TYPES.contains(returnType) || returnType.isEnum()) {
-          String value = Iterables.getOnlyElement(entry.getValue());
-          Preconditions.checkArgument(returnType.equals(String.class) || !value.isEmpty(),
-              "Empty argument value is only allowed for String, String Array, and Collection,"
-               + " but received: " + returnType);
-          convertedOptions.put(entry.getKey(), MAPPER.convertValue(value, type));
-        } else {
-          String value = Iterables.getOnlyElement(entry.getValue());
-          Preconditions.checkArgument(returnType.equals(String.class) || !value.isEmpty(),
-              "Empty argument value is only allowed for String, String Array, and Collection,"
-               + " but received: " + returnType);
-          try {
-            convertedOptions.put(entry.getKey(), MAPPER.readValue(value, type));
-          } catch (IOException e) {
-            throw new IllegalArgumentException("Unable to parse JSON value " + value, e);
-          }
-        }
-      } catch (IllegalArgumentException e) {
-        if (strictParsing) {
-          throw e;
-        } else {
-          LOG.warn("Strict parsing is disabled, ignoring option '{}' with value '{}' because {}",
-              entry.getKey(), entry.getValue(), e.getMessage());
-        }
-      }
-    }
-    return convertedOptions;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/0393a791/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsRegistrar.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsRegistrar.java b/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsRegistrar.java
deleted file mode 100644
index 3fbe7ca..0000000
--- a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsRegistrar.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.cloud.dataflow.sdk.options;
-
-import com.google.auto.service.AutoService;
-
-import java.util.ServiceLoader;
-
-/**
- * {@link PipelineOptions} creators have the ability to automatically have their
- * {@link PipelineOptions} registered with this SDK by creating a {@link ServiceLoader} entry
- * and a concrete implementation of this interface.
- *
- * <p>Note that automatic registration of any {@link PipelineOptions} requires users
- * conform to the limitations discussed on {@link PipelineOptionsFactory#register(Class)}.
- *
- * <p>It is optional but recommended to use one of the many build time tools such as
- * {@link AutoService} to generate the necessary META-INF files automatically.
- */
-public interface PipelineOptionsRegistrar {
-  Iterable<Class<? extends PipelineOptions>> getPipelineOptions();
-}

http://git-wip-us.apache.org/repos/asf/incubator-beam/blob/0393a791/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsValidator.java
----------------------------------------------------------------------
diff --git a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsValidator.java b/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsValidator.java
deleted file mode 100644
index 9143c0b..0000000
--- a/sdks/java/core/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsValidator.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.cloud.dataflow.sdk.options;
-
-import com.google.cloud.dataflow.sdk.options.Validation.Required;
-import com.google.cloud.dataflow.sdk.util.common.ReflectHelpers;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.SortedSetMultimap;
-import com.google.common.collect.TreeMultimap;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.Collection;
-
-/**
- * Validates that the {@link PipelineOptions} conforms to all the {@link Validation} criteria.
- */
-public class PipelineOptionsValidator {
-  /**
-   * Validates that the passed {@link PipelineOptions} conforms to all the validation criteria from
-   * the passed in interface.
-   *
-   * <p>Note that the interface requested must conform to the validation criteria specified on
-   * {@link PipelineOptions#as(Class)}.
-   *
-   * @param klass The interface to fetch validation criteria from.
-   * @param options The {@link PipelineOptions} to validate.
-   * @return The type
-   */
-  public static <T extends PipelineOptions> T validate(Class<T> klass, PipelineOptions options) {
-    Preconditions.checkNotNull(klass);
-    Preconditions.checkNotNull(options);
-    Preconditions.checkArgument(Proxy.isProxyClass(options.getClass()));
-    Preconditions.checkArgument(Proxy.getInvocationHandler(options)
-        instanceof ProxyInvocationHandler);
-
-    // Ensure the methods for T are registered on the ProxyInvocationHandler
-    T asClassOptions = options.as(klass);
-
-    ProxyInvocationHandler handler =
-        (ProxyInvocationHandler) Proxy.getInvocationHandler(asClassOptions);
-
-    SortedSetMultimap<String, Method> requiredGroups = TreeMultimap.create(
-        Ordering.natural(), PipelineOptionsFactory.MethodNameComparator.INSTANCE);
-    for (Method method : ReflectHelpers.getClosureOfMethodsOnInterface(klass)) {
-      Required requiredAnnotation = method.getAnnotation(Validation.Required.class);
-      if (requiredAnnotation != null) {
-        if (requiredAnnotation.groups().length > 0) {
-          for (String requiredGroup : requiredAnnotation.groups()) {
-            requiredGroups.put(requiredGroup, method);
-          }
-        } else {
-          Preconditions.checkArgument(handler.invoke(asClassOptions, method, null) != null,
-              "Missing required value for [" + method + ", \"" + getDescription(method) + "\"]. ");
-        }
-      }
-    }
-
-    for (String requiredGroup : requiredGroups.keySet()) {
-      if (!verifyGroup(handler, asClassOptions, requiredGroups.get(requiredGroup))) {
-        throw new IllegalArgumentException("Missing required value for group [" + requiredGroup
-            + "]. At least one of the following properties "
-            + Collections2.transform(
-                requiredGroups.get(requiredGroup), ReflectHelpers.METHOD_FORMATTER)
-            + " required. Run with --help=" + klass.getSimpleName() + " for more information.");
-      }
-    }
-
-    return asClassOptions;
-  }
-
-  private static boolean verifyGroup(ProxyInvocationHandler handler, PipelineOptions options,
-      Collection<Method> requiredGroup) {
-    for (Method m : requiredGroup) {
-      if (handler.invoke(options, m, null) != null) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private static String getDescription(Method method) {
-    Description description = method.getAnnotation(Description.class);
-    return description == null ? "" : description.value();
-  }
-}



Mime
View raw message