http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/ArgumentInfo.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/com/twitter/common/args/ArgumentInfo.java 
b/commons/src/main/java/com/twitter/common/args/ArgumentInfo.java
new file mode 100644
index 0000000..15daa11
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/ArgumentInfo.java
@@ -0,0 +1,250 @@
+// 
=================================================================================================
+// Copyright 2012 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.reflect.TypeToken;
+
+import com.twitter.common.args.constraints.NotNullVerifier;
+import com.twitter.common.base.MorePreconditions;
+
+/**
+ * Description of a command line {@link Arg} instance.
+ */
+public abstract class ArgumentInfo<T> {
+  static final ImmutableSet<String> HELP_ARGS = ImmutableSet.of("h", "help");
+
+  /**
+   * Extracts the {@code Arg} from the given field.
+   *
+   * @param field The field containing the {@code Arg}.
+   * @param instance An optional object instance containing the field.
+   * @return The extracted {@code} Arg.
+   * @throws IllegalArgumentException If the field does not contain an arg.
+   */
+  protected static Arg<?> getArgForField(Field field, Optional<?> instance) {
+    Preconditions.checkArgument(field.getType() == Arg.class,
+        "Field is annotated for argument parsing but is not of Arg type: " + 
field);
+    Preconditions.checkArgument(Modifier.isStatic(field.getModifiers()) || 
instance.isPresent(),
+        "Non-static argument fields are not supported, found " + field);
+
+    field.setAccessible(true);
+    try {
+      return (Arg<?>) field.get(instance.orNull());
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException("Cannot get arg value for " + field);
+    }
+  }
+
+  private final String canonicalName;
+  private final String name;
+  private final String help;
+  private final boolean argFile;
+  private final Arg<T> arg;
+  private final TypeToken<T> type;
+  private final List<Annotation> verifierAnnotations;
+  @Nullable private final Class<? extends Parser<? extends T>> parser;
+
+  /**
+   * Creates a new {@code ArgsInfo}.
+   *
+   * @param canonicalName A fully qualified name for the argument.
+   * @param name The simple name for the argument.
+   * @param help Help string.
+   * @param argFile If argument file is allowed.
+   * @param arg Argument object.
+   * @param type Concrete argument type.
+   * @param verifierAnnotations {@link com.twitter.common.args.Verifier} 
annotations for this
+   *     argument.
+   * @param parser Parser for the argument type.
+   */
+  protected ArgumentInfo(
+      String canonicalName,
+      String name,
+      String help,
+      boolean argFile,
+      Arg<T> arg,
+      TypeToken<T> type,
+      List<Annotation> verifierAnnotations,
+      @Nullable Class<? extends Parser<? extends T>> parser) {
+
+    this.canonicalName = MorePreconditions.checkNotBlank(canonicalName);
+    this.name = MorePreconditions.checkNotBlank(name);
+    this.help = MorePreconditions.checkNotBlank(help);
+    this.argFile = argFile;
+    this.arg = Preconditions.checkNotNull(arg);
+    this.type = Preconditions.checkNotNull(type);
+    this.verifierAnnotations = ImmutableList.copyOf(verifierAnnotations);
+    this.parser = parser;
+  }
+
+  /**
+   * Return the name of the command line argument. In an optional argument, 
this is expressed on
+   * the command line by "-name=value"; whereas, for a positional argument, 
the name indicates
+   * the type/function.
+   */
+  public final String getName() {
+    return name;
+  }
+
+  /**
+   * Return the fully-qualified name of the command line argument. This is 
used as a command-line
+   * optional argument, as in: -prefix.name=value. Prefix is typically a java 
package and class like
+   * "com.twitter.myapp.MyClass". The difference between a canonical name and 
a regular name is that
+   * it is in some circumstances for two names to collide; the canonical name, 
then, disambiguates.
+   */
+  public final String getCanonicalName() {
+    return canonicalName;
+  }
+
+  /**
+   * Returns the instructions for this command-line argument. This is 
typically used when the
+   * executable is passed the -help flag.
+   */
+  public String getHelp() {
+    return help;
+  }
+
+  /**
+   * Returns whether an argument file is allowed for this argument.
+   */
+  public boolean argFile() {
+    return argFile;
+  }
+
+  /**
+   * Returns the Arg associated with this command-line argument. The Arg<?> is 
a mutable container
+   * cell that holds the value passed-in on the command line, after parsing 
and validation.
+   */
+  public Arg<T> getArg() {
+    return arg;
+  }
+
+  /**
+   * Sets the value of the {@link Arg} described by this {@code ArgumentInfo}.
+   *
+   * @param value The value to set.
+   */
+  protected void setValue(@Nullable T value) {
+    arg.set(value);
+  }
+
+  /**
+   * Returns the TypeToken that represents the type of this command-line 
argument.
+   */
+  public TypeToken<T> getType() {
+    return type;
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    return (object instanceof ArgumentInfo) && arg.equals(((ArgumentInfo) 
object).arg);
+  }
+
+  @Override
+  public int hashCode() {
+    return arg.hashCode();
+  }
+
+  /**
+   * Finds an appropriate parser for this args underlying value type.
+   *
+   * @param parserOracle The registry of known parsers.
+   * @return A parser that can parse strings into the underlying argument type.
+   * @throws IllegalArgumentException If no parser was found for the 
underlying argument type.
+   */
+  protected Parser<? extends T> getParser(ParserOracle parserOracle) {
+    Preconditions.checkNotNull(parserOracle);
+    if (parser == null || NoParser.class.equals(parser)) {
+      return parserOracle.get(type);
+    } else {
+      try {
+        return parser.newInstance();
+      } catch (InstantiationException e) {
+        throw new RuntimeException("Failed to instantiate parser " + parser);
+      } catch (IllegalAccessException e) {
+        throw new RuntimeException("No access to instantiate parser " + 
parser);
+      }
+    }
+  }
+
+  static class ValueVerifier<T> {
+    private final Verifier<? super T> verifier;
+    private final Annotation annotation;
+
+    ValueVerifier(Verifier<? super T> verifier, Annotation annotation) {
+      this.verifier = verifier;
+      this.annotation = annotation;
+    }
+
+    void verify(@Nullable T value) {
+      if (value != null || verifier instanceof NotNullVerifier) {
+        verifier.verify(value, annotation);
+      }
+    }
+
+    String toString(Class<? extends T> rawType) {
+      return verifier.toString(rawType, annotation);
+    }
+  }
+
+  private Iterable<ValueVerifier<T>> getVerifiers(final Verifiers 
verifierOracle) {
+    Function<Annotation, Optional<ValueVerifier<T>>> toVerifier =
+        new Function<Annotation, Optional<ValueVerifier<T>>>() {
+          @Override public Optional<ValueVerifier<T>> apply(Annotation 
annotation) {
+            @Nullable Verifier<? super T> verifier = verifierOracle.get(type, 
annotation);
+            if (verifier != null) {
+              return Optional.of(new ValueVerifier<T>(verifier, annotation));
+            } else {
+              return Optional.absent();
+            }
+          }
+        };
+    return Optional.presentInstances(Iterables.transform(verifierAnnotations, 
toVerifier));
+  }
+
+  void verify(Verifiers verifierOracle) {
+    @Nullable T value = getArg().uncheckedGet();
+    for (ValueVerifier<T> valueVerifier : getVerifiers(verifierOracle)) {
+      valueVerifier.verify(value);
+    }
+  }
+
+  ImmutableList<String> collectConstraints(Verifiers verifierOracle) {
+    @SuppressWarnings("unchecked") // type.getType() is T
+    final Class<? extends T> rawType = (Class<? extends T>) type.getRawType();
+    return FluentIterable.from(getVerifiers(verifierOracle)).transform(
+        new Function<ValueVerifier<T>, String>() {
+          @Override public String apply(ValueVerifier<T> verifier) {
+            return verifier.toString(rawType);
+          }
+        }).toList();
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/OptionInfo.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/com/twitter/common/args/OptionInfo.java 
b/commons/src/main/java/com/twitter/common/args/OptionInfo.java
new file mode 100644
index 0000000..1f38195
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/OptionInfo.java
@@ -0,0 +1,204 @@
+// 
=================================================================================================
+// Copyright 2012 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
+import com.google.common.base.Strings;
+import com.google.common.io.Files;
+import com.google.common.reflect.TypeToken;
+
+import com.twitter.common.args.apt.Configuration;
+import com.twitter.common.base.Function;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Description of a command line option/flag such as -foo=bar.
+ */
+public final class OptionInfo<T> extends ArgumentInfo<T> {
+  static final String ARG_NAME_RE = "[\\w\\-\\.]+";
+  static final String ARG_FILE_HELP_TEMPLATE
+      = "%s This argument supports @argfile format. See details below.";
+
+  private static final Pattern ARG_NAME_PATTERN = Pattern.compile(ARG_NAME_RE);
+  private static final String NEGATE_BOOLEAN = "no_";
+  private static final String ARG_FILE_INDICATOR = "@";
+
+  /**
+   * Factory method to create a OptionInfo from a field.
+   *
+   * @param field The field must contain a {@link Arg}.
+   * @return an OptionInfo describing the field.
+   */
+  static OptionInfo<?> createFromField(Field field) {
+    return createFromField(field, null);
+  }
+
+  /**
+   * Factory method to create a OptionInfo from a field.
+   *
+   * @param field The field must contain a {@link Arg}.
+   * @param instance The object containing the non-static Arg instance or else 
null if the Arg
+   *     field is static.
+   * @return an OptionInfo describing the field.
+   */
+  static OptionInfo<?> createFromField(final Field field, @Nullable Object 
instance) {
+    CmdLine cmdLine = field.getAnnotation(CmdLine.class);
+    if (cmdLine == null) {
+      throw new Configuration.ConfigurationException(
+          "No @CmdLine Arg annotation for field " + field);
+    }
+
+    String name = cmdLine.name();
+    Preconditions.checkNotNull(name);
+    checkArgument(!HELP_ARGS.contains(name),
+        String.format("Argument name '%s' is reserved for builtin argument 
help", name));
+    checkArgument(ARG_NAME_PATTERN.matcher(name).matches(),
+        String.format("Argument name '%s' does not match required pattern %s",
+            name, ARG_NAME_RE));
+
+    Function<String, String> canonicalizer = new Function<String, String>() {
+      @Override public String apply(String name) {
+        return field.getDeclaringClass().getCanonicalName() + "." + name;
+      }
+    };
+
+    @SuppressWarnings({"unchecked", "rawtypes"}) // we have no way to know the 
type here
+    OptionInfo<?> optionInfo = new OptionInfo(
+        canonicalizer,
+        name,
+        getCmdLineHelp(cmdLine),
+        cmdLine.argFile(),
+        ArgumentInfo.getArgForField(field, Optional.fromNullable(instance)),
+        TypeUtil.getTypeParamTypeToken(field),
+        Arrays.asList(field.getAnnotations()),
+        cmdLine.parser());
+
+    return optionInfo;
+  }
+
+  private static String getCmdLineHelp(CmdLine cmdLine) {
+    String help = cmdLine.help();
+
+    if (cmdLine.argFile()) {
+      help = String.format(ARG_FILE_HELP_TEMPLATE, help, cmdLine.name(), 
cmdLine.name());
+    }
+
+    return help;
+  }
+
+  private final Function<String, String> canonicalizer;
+
+  private OptionInfo(
+      Function<String, String> canonicalizer,
+      String name,
+      String help,
+      boolean argFile,
+      Arg<T> arg,
+      TypeToken<T> type,
+      List<Annotation> verifierAnnotations,
+      @Nullable Class<? extends Parser<T>> parser) {
+
+    super(canonicalizer.apply(name), name, help, argFile, arg, type,
+        verifierAnnotations, parser);
+    this.canonicalizer = canonicalizer;
+  }
+
+  /**
+   * Parses the value and store result in the {@link Arg} contained in this 
{@code OptionInfo}.
+   */
+  void load(ParserOracle parserOracle, String optionName, String value) {
+    Parser<? extends T> parser = getParser(parserOracle);
+
+    String finalValue = value;
+
+    // If "-arg=@file" is allowed and specified, then we read the value from 
the file
+    // and use it as the raw value to be parsed for the argument.
+    if (argFile()
+        && !Strings.isNullOrEmpty(value)
+        && value.startsWith(ARG_FILE_INDICATOR)) {
+      finalValue = getArgFileContent(optionName, 
value.substring(ARG_FILE_INDICATOR.length()));
+    }
+
+    Object result = parser.parse(parserOracle, getType().getType(), 
finalValue); // [A]
+
+    // If the arg type is boolean, check if the command line uses the negated 
boolean form.
+    if (isBoolean()) {
+      if (Predicates.in(Arrays.asList(getNegatedName(), 
getCanonicalNegatedName()))
+          .apply(optionName)) {
+        result = !(Boolean) result; // [B]
+      }
+    }
+
+    // We know result is T at line [A] but throw this type information away to 
allow negation if T
+    // is Boolean at line [B]
+    @SuppressWarnings("unchecked")
+    T parsed = (T) result;
+
+    setValue(parsed);
+  }
+
+  boolean isBoolean() {
+    return getType().getRawType() == Boolean.class;
+  }
+
+  /**
+   * Similar to the simple name, but with boolean arguments appends "no_", as 
in:
+   * {@code -no_fire=false}
+   */
+  String getNegatedName() {
+    return NEGATE_BOOLEAN + getName();
+  }
+
+  /**
+   * Similar to the canonical name, but with boolean arguments appends "no_", 
as in:
+   * {@code -com.twitter.common.MyApp.no_fire=false}
+   */
+  String getCanonicalNegatedName() {
+    return canonicalizer.apply(getNegatedName());
+  }
+
+  private String getArgFileContent(String optionName, String argFilePath)
+      throws IllegalArgumentException {
+    if (argFilePath.isEmpty()) {
+      throw new IllegalArgumentException(
+          String.format("Invalid null/empty value for argument '%s'.", 
optionName));
+    }
+
+    try {
+      return Files.toString(new File(argFilePath), Charsets.UTF_8);
+    } catch (IOException e) {
+      throw new IllegalArgumentException(
+          String.format("Unable to read argument '%s' value from file '%s'.",
+              optionName, argFilePath),
+          e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/Parsers.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/com/twitter/common/args/Parsers.java 
b/commons/src/main/java/com/twitter/common/args/Parsers.java
new file mode 100644
index 0000000..c879294
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/Parsers.java
@@ -0,0 +1,117 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.reflect.TypeToken;
+
+import com.twitter.common.args.apt.Configuration;
+import com.twitter.common.args.apt.Configuration.ParserInfo;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import static com.twitter.common.args.apt.Configuration.ConfigurationException;
+
+/**
+ * A registry of Parsers for different supported argument types.
+ *
+ * @author William Farner
+ */
+public final class Parsers implements ParserOracle {
+
+  public static final Splitter MULTI_VALUE_SPLITTER =
+      Splitter.on(",").trimResults().omitEmptyStrings();
+
+  private static final Function<ParserInfo, Class<?>> INFO_TO_PARSED_TYPE =
+      new Function<ParserInfo, Class<?>>() {
+        @Override public Class<?> apply(ParserInfo parserInfo) {
+          try {
+            return Class.forName(parserInfo.parsedType);
+          } catch (ClassNotFoundException e) {
+            throw new ConfigurationException(e);
+          }
+        }
+      };
+
+  @VisibleForTesting
+  static final Function<ParserInfo, Parser<?>> INFO_TO_PARSER =
+      new Function<ParserInfo, Parser<?>>() {
+        @Override public Parser<?> apply(ParserInfo parserInfo) {
+          try {
+            Class<?> parserClass = Class.forName(parserInfo.parserClass);
+            Constructor<?> constructor = parserClass.getDeclaredConstructor();
+            constructor.setAccessible(true);
+            return (Parser<?>) constructor.newInstance();
+          } catch (ClassNotFoundException e) {
+            throw new ConfigurationException(e);
+          } catch (InstantiationException e) {
+            throw new ConfigurationException(e);
+          } catch (IllegalAccessException e) {
+            throw new ConfigurationException(e);
+          } catch (NoSuchMethodException e) {
+            throw new ConfigurationException(e);
+          } catch (InvocationTargetException e) {
+            throw new ConfigurationException(e);
+          }
+        }
+      };
+
+  private final ImmutableMap<Class<?>, Parser<?>> registry;
+
+  /**
+   * Creates a new parser registry over the specified {@code parsers}.
+   *
+   * @param parsers The parsers to register.
+   */
+  public Parsers(Map<Class<?>, Parser<?>> parsers) {
+    Preconditions.checkNotNull(parsers);
+    registry = ImmutableMap.copyOf(parsers);
+  }
+
+  @Override
+  public <T> Parser<T> get(TypeToken<T> type) throws IllegalArgumentException {
+    Parser<?> parser;
+    Class<?> explicitClass = type.getRawType();
+    while (((parser = registry.get(explicitClass)) == null) && (explicitClass 
!= null)) {
+      explicitClass = explicitClass.getSuperclass();
+    }
+    checkArgument(parser != null, "No parser found for " + type);
+
+    // We control loading of the registry which ensures a proper mapping of 
class -> parser
+    @SuppressWarnings("unchecked")
+    Parser<T> parserT = (Parser<T>) parser;
+
+    return parserT;
+  }
+
+  static Parsers fromConfiguration(Configuration configuration) {
+    Map<Class<?>, Parser<?>> parsers =
+        Maps.transformValues(
+            Maps.uniqueIndex(configuration.parserInfo(), INFO_TO_PARSED_TYPE),
+            INFO_TO_PARSER);
+    return new Parsers(parsers);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/PositionalInfo.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/com/twitter/common/args/PositionalInfo.java 
b/commons/src/main/java/com/twitter/common/args/PositionalInfo.java
new file mode 100644
index 0000000..3a65cc4
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/PositionalInfo.java
@@ -0,0 +1,119 @@
+// 
=================================================================================================
+// Copyright 2012 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.reflect.TypeToken;
+
+import com.twitter.common.args.apt.Configuration;
+
+/**
+ * Description of a positional command line argument.
+ */
+public final class PositionalInfo<T> extends ArgumentInfo<List<T>> {
+  /**
+   * Factory method to create a PositionalInfo from a field.
+   *
+   * @param field The field must contain a {@link Arg 
Arg&lt;List&lt;?&gt;&gt;}. The List&lt;?&gt;
+   *     represents zero or more positional arguments.
+   * @return a PositionalInfo describing the field.
+   */
+  static PositionalInfo<?> createFromField(Field field) {
+    return createFromField(field, null);
+  }
+
+  /**
+   * Factory method to create a PositionalInfo from a field.
+   *
+   * @param field The field must contain a {@link Arg 
Arg&lt;List&lt;?&gt;&gt;}. The List&lt;?&gt;
+   *     represents zero or more positional arguments.
+   * @param instance The object containing the non-static Arg instance or else 
null if the Arg
+   *     field is static.
+   * @return a PositionalInfo describing the field.
+   */
+  static PositionalInfo<?> createFromField(Field field, @Nullable Object 
instance) {
+    Preconditions.checkNotNull(field);
+    Positional positional = field.getAnnotation(Positional.class);
+    if (positional == null) {
+      throw new Configuration.ConfigurationException(
+          "No @Positional Arg annotation for field " + field);
+    }
+
+    Preconditions.checkArgument(
+        TypeUtil.getRawType(TypeUtil.getTypeParam(field)) == List.class,
+        "Field is annotated for positional parsing but is not of Arg<List<?>> 
type");
+    Type nestedType = TypeUtil.extractTypeToken(TypeUtil.getTypeParam(field));
+
+    @SuppressWarnings({"unchecked", "rawtypes"}) // we have no way to know the 
type here
+    PositionalInfo<?> positionalInfo = new PositionalInfo(
+        field.getDeclaringClass().getCanonicalName() + "." + field.getName(),
+        "[positional args]",
+        positional.help(),
+        ArgumentInfo.getArgForField(field, Optional.fromNullable(instance)),
+        TypeUtil.getTypeParamTypeToken(field),
+        TypeToken.of(nestedType),
+        Arrays.asList(field.getAnnotations()),
+        positional.parser());
+
+    return positionalInfo;
+  }
+
+  private final TypeToken<T> elementType;
+
+  private PositionalInfo(
+      String canonicalName,
+      String name,
+      String help,
+      Arg<List<T>> arg,
+      TypeToken<List<T>> type,
+      TypeToken<T> elementType,
+      List<Annotation> verifierAnnotations,
+      @Nullable Class<? extends Parser<? extends List<T>>> parser) {
+
+    // TODO: https://github.com/twitter/commons/issues/353, consider future 
support of
+    // argFile for Positional arguments.
+    super(canonicalName, name, help, false, arg, type, verifierAnnotations, 
parser);
+    this.elementType = elementType;
+  }
+
+  /**
+   * Parses the positional args and stores the results in the {@link Arg} 
described by this
+   * {@code PositionalInfo}.
+   */
+  void load(final ParserOracle parserOracle, List<String> positionalArgs) {
+    final Parser<? extends T> parser = parserOracle.get(elementType);
+    List<T> assignmentValue = 
Lists.newArrayList(Iterables.transform(positionalArgs,
+      new Function<String, T>() {
+        @Override public T apply(String argValue) {
+          return parser.parse(parserOracle, elementType.getType(), argValue);
+        }
+      }));
+    setValue(assignmentValue);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/TypeUtil.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/com/twitter/common/args/TypeUtil.java 
b/commons/src/main/java/com/twitter/common/args/TypeUtil.java
new file mode 100644
index 0000000..a0e29ae
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/TypeUtil.java
@@ -0,0 +1,123 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.Arrays;
+import java.util.List;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * Utility class to extract generic type information.
+ *
+ * TODO(William Farner): Move this into a common library, integrate with 
EasyMockTest.Clazz.
+ *
+ * @author William Farner
+ */
+public final class TypeUtil {
+
+  private static final Function<Type, Type> GET_TYPE = new Function<Type, 
Type>() {
+    @Override public Type apply(Type type) {
+      if (type instanceof WildcardType) {
+        return apply(((WildcardType) type).getUpperBounds()[0]);
+      }
+      return type;
+    }
+  };
+
+  private TypeUtil() {
+    // Utility.
+  }
+
+  /**
+   * Gets the types that a type is type-parameterized with, in declaration 
order.
+   *
+   * @param type The type to extract type parameters from.
+   * @return The types that {@code type} is parameterized with.
+   */
+  public static List<Type> getTypeParams(Type type) {
+    if (type instanceof WildcardType) {
+      return getTypeParams(GET_TYPE.apply(type));
+    }
+    return Lists.transform(Arrays.asList(
+        ((ParameterizedType) type).getActualTypeArguments()), GET_TYPE);
+  }
+
+  /**
+   * Finds the raw class of type.
+   *
+   * @param type The type to get the raw class of.
+   * @return The raw class of type.
+   */
+  public static Class<?> getRawType(Type type) {
+    if (type instanceof ParameterizedType) {
+      return getRawType(((ParameterizedType) type).getRawType());
+    }
+    if (type instanceof WildcardType) {
+      return getRawType(((WildcardType) type).getUpperBounds()[0]);
+    }
+    return (Class<?>) type;
+  }
+
+  /**
+   * Convenience method to call {@link #getTypeParam(Field)}, with the 
requirement that there
+   * is exactly one type parameter on the field.
+   *
+   * @param field The field to extract type parameters from.
+   * @return The raw classes of types that {@code field} is parameterized with.
+   */
+  public static TypeToken<?> getTypeParamTypeToken(Field field) {
+    List<Type> typeParams = getTypeParams(field.getGenericType());
+    Preconditions.checkArgument(typeParams.size() == 1,
+        "Expected exactly one type parameter for field " + field);
+    return TypeToken.of(typeParams.get(0));
+  }
+
+  /**
+   * Gets the type parameter from a field.  Assumes that there is at least one 
type parameter.
+   *
+   * @param field The field to extract the type parameter from.
+   * @return The field type parameter.
+   */
+  public static Type getTypeParam(Field field) {
+    return extractTypeToken(field.getGenericType());
+  }
+
+  /**
+   * Extracts the actual type parameter for a singly parameterized type.
+   *
+   * @param type The parameterized type to extract the type argument from.
+   * @return The type of the single specified type parameter for {@code type}.
+   * @throws IllegalArgumentException if the supplied type does not have 
exactly one specified type
+   *     parameter
+   */
+  public static Type extractTypeToken(Type type) {
+    Preconditions.checkNotNull(type);
+    Preconditions.checkArgument(type instanceof ParameterizedType, "Missing 
type parameter.");
+    Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
+    Preconditions.checkArgument(typeArguments.length == 1,
+        "Expected a type with exactly 1 type argument");
+    return typeArguments[0];
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/Verifiers.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/com/twitter/common/args/Verifiers.java 
b/commons/src/main/java/com/twitter/common/args/Verifiers.java
new file mode 100644
index 0000000..adc75c2
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/Verifiers.java
@@ -0,0 +1,95 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+
+import com.twitter.common.args.apt.Configuration;
+import com.twitter.common.collections.Pair;
+
+import static com.twitter.common.args.apt.Configuration.ConfigurationException;
+import static com.twitter.common.args.apt.Configuration.VerifierInfo;
+
+/**
+ * Utility class to manage relationships between constraints and types.
+ *
+ * @author William Farner
+ */
+public final class Verifiers {
+
+  private final ImmutableMap<Pair<Class<?>, Class<? extends Annotation>>,
+                             Verifier<?>> registry;
+
+  private Verifiers(Map<Pair<Class<?>, Class<? extends Annotation>>,
+                        Verifier<?>> registry) {
+
+    this.registry = ImmutableMap.copyOf(registry);
+  }
+
+  @Nullable
+  <T> Verifier<T> get(TypeToken<T> type, Annotation constraint) {
+    for (Map.Entry<Pair<Class<?>, Class<? extends Annotation>>, Verifier<?>> 
entry
+        : registry.entrySet()) {
+      if (entry.getKey().getSecond() == constraint.annotationType()
+          && entry.getKey().getFirst().isAssignableFrom(type.getRawType())) {
+
+        // We control the registry which ensures a proper mapping of class -> 
verifier.
+        @SuppressWarnings("unchecked")
+        Verifier<T> verifier = (Verifier<T>) entry.getValue();
+        return verifier;
+      }
+    }
+
+    return null;
+  }
+
+  static Verifiers fromConfiguration(Configuration configuration) {
+    ImmutableMap.Builder<Pair<Class<?>, Class<? extends Annotation>>,
+                         Verifier<?>> registry = ImmutableMap.builder();
+
+    for (VerifierInfo info : configuration.verifierInfo()) {
+      Class<?> verifiedType = forName(info.verifiedType);
+      Class<? extends Annotation> verifyingAnnotation = 
forName(info.verifyingAnnotation);
+      Class<? extends Verifier<?>> verifierClass = forName(info.verifierClass);
+      try {
+        registry.put(
+            Pair.<Class<?>, Class<? extends Annotation>>of(verifiedType, 
verifyingAnnotation),
+          verifierClass.newInstance());
+      } catch (InstantiationException e) {
+        throw new ConfigurationException(e);
+      } catch (IllegalAccessException e) {
+        throw new ConfigurationException(e);
+      }
+    }
+    return new Verifiers(registry.build());
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <T> Class<T> forName(String name) {
+    try {
+      return (Class<T>) Class.forName(name);
+    } catch (ClassNotFoundException e) {
+      throw new ConfigurationException(e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/CanExecute.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/CanExecute.java 
b/commons/src/main/java/com/twitter/common/args/constraints/CanExecute.java
new file mode 100644
index 0000000..1804674
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/CanExecute.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates an entity must be executable.
+ *
+ * @author Steven Nie
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface CanExecute {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/CanExecuteFileVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/CanExecuteFileVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/CanExecuteFileVerifier.java
new file mode 100644
index 0000000..4486351
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/CanExecuteFileVerifier.java
@@ -0,0 +1,43 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifier to ensure that a file is executable.
+ *
+ * @author Steven Nie
+ */
+@VerifierFor(CanExecute.class)
+public class CanExecuteFileVerifier implements Verifier<File> {
+  @Override
+  public void verify(File value, Annotation annotation) {
+    checkArgument(value.canExecute(), "File must be executable");
+  }
+
+  @Override
+  public String toString(Class<? extends File> argType, Annotation annotation) 
{
+    return "file must be executable";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/CanRead.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/CanRead.java 
b/commons/src/main/java/com/twitter/common/args/constraints/CanRead.java
new file mode 100644
index 0000000..178e3ec
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/CanRead.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates an entity must be readable.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface CanRead {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/CanReadFileVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/CanReadFileVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/CanReadFileVerifier.java
new file mode 100644
index 0000000..2fe4415
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/CanReadFileVerifier.java
@@ -0,0 +1,43 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifier to ensure that a file is readable.
+ *
+ * @author William Farner
+ */
+@VerifierFor(CanRead.class)
+public class CanReadFileVerifier implements Verifier<File> {
+  @Override
+  public void verify(File value, Annotation annotation) {
+    checkArgument(value.canRead(), "File must be readable");
+  }
+
+  @Override
+  public String toString(Class<? extends File> argType, Annotation annotation) 
{
+    return "file must be readable";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/CanWrite.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/CanWrite.java 
b/commons/src/main/java/com/twitter/common/args/constraints/CanWrite.java
new file mode 100644
index 0000000..3ea346a
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/CanWrite.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates an entity must be writable.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface CanWrite {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/CanWriteFileVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/CanWriteFileVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/CanWriteFileVerifier.java
new file mode 100644
index 0000000..08f1483
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/CanWriteFileVerifier.java
@@ -0,0 +1,43 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifier to ensure that a file can be written to.
+ *
+ * @author William Farner
+ */
+@VerifierFor(CanWrite.class)
+public class CanWriteFileVerifier implements Verifier<File> {
+  @Override
+  public void verify(File value, Annotation annotation) {
+    checkArgument(value.canWrite(), "File must be writable.");
+  }
+
+  @Override
+  public String toString(Class<? extends File> argType, Annotation annotation) 
{
+    return "file must be writeable";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/Exists.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/Exists.java 
b/commons/src/main/java/com/twitter/common/args/constraints/Exists.java
new file mode 100644
index 0000000..f85ccce
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/Exists.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates an entity must exist.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface Exists {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/ExistsFileVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/ExistsFileVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/ExistsFileVerifier.java
new file mode 100644
index 0000000..c430997
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/ExistsFileVerifier.java
@@ -0,0 +1,43 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifier to ensure that a file exists.
+ *
+ * @author William Farner
+ */
+@VerifierFor(Exists.class)
+public class ExistsFileVerifier implements Verifier<File> {
+  @Override
+  public void verify(File value, Annotation annotation) {
+    checkArgument(value.exists(), "file must exist");
+  }
+
+  @Override
+  public String toString(Class<? extends File> argType, Annotation annotation) 
{
+    return "file must exist";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/IsDirectory.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/IsDirectory.java 
b/commons/src/main/java/com/twitter/common/args/constraints/IsDirectory.java
new file mode 100644
index 0000000..361cf30
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/IsDirectory.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates an entity must represent a directory.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface IsDirectory {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/IsDirectoryFileVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/IsDirectoryFileVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/IsDirectoryFileVerifier.java
new file mode 100644
index 0000000..f08a20d
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/IsDirectoryFileVerifier.java
@@ -0,0 +1,43 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifier to ensure that a file is a directory.
+ *
+ * @author William Farner
+ */
+@VerifierFor(IsDirectory.class)
+public class IsDirectoryFileVerifier implements Verifier<File> {
+  @Override
+  public void verify(File value, Annotation annotation) {
+    checkArgument(value.isDirectory(), "Must be a directory.");
+  }
+
+  @Override
+  public String toString(Class<? extends File> argType, Annotation annotation) 
{
+    return "file must be a directory";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/NotEmpty.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/NotEmpty.java 
b/commons/src/main/java/com/twitter/common/args/constraints/NotEmpty.java
new file mode 100644
index 0000000..86c52d4
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/NotEmpty.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates that an entity is non-empty.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface NotEmpty {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/NotEmptyIterableVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/NotEmptyIterableVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/NotEmptyIterableVerifier.java
new file mode 100644
index 0000000..10d204b
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/NotEmptyIterableVerifier.java
@@ -0,0 +1,44 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Annotation;
+
+import com.google.common.collect.Iterables;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifies that an iterable is not empty.
+ *
+ * @author William Farner
+ */
+@VerifierFor(NotEmpty.class)
+public class NotEmptyIterableVerifier implements Verifier<Iterable<?>> {
+  @Override
+  public void verify(Iterable<?> value, Annotation annotation) {
+    checkArgument(!Iterables.isEmpty(value), "Value must not be empty.");
+  }
+
+  @Override
+  public String toString(Class<? extends Iterable<?>> argType, Annotation 
annotation) {
+    return "must have at least 1 item";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/NotEmptyStringVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/NotEmptyStringVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/NotEmptyStringVerifier.java
new file mode 100644
index 0000000..b377b52
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/NotEmptyStringVerifier.java
@@ -0,0 +1,42 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifier to ensure that a string is not empty.
+ *
+ * @author William Farner
+ */
+@VerifierFor(NotEmpty.class)
+public class NotEmptyStringVerifier implements Verifier<String> {
+  @Override
+  public void verify(String s, Annotation annotation) {
+    checkArgument(!s.isEmpty(), "Value must not be empty.");
+  }
+
+  @Override
+  public String toString(Class<? extends String> argType, Annotation 
annotation) {
+    return "must be non-empty";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/NotNegative.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/NotNegative.java 
b/commons/src/main/java/com/twitter/common/args/constraints/NotNegative.java
new file mode 100644
index 0000000..3733103
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/NotNegative.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates that an entity is non-negative.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface NotNegative {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/NotNegativeNumberVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/NotNegativeNumberVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/NotNegativeNumberVerifier.java
new file mode 100644
index 0000000..7dc2987
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/NotNegativeNumberVerifier.java
@@ -0,0 +1,42 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifier to ensure that a number is non-negative.
+ *
+ * @author William Farner
+ */
+@VerifierFor(NotNegative.class)
+public class NotNegativeNumberVerifier implements Verifier<Number> {
+  @Override
+  public void verify(Number number, Annotation annotation) {
+    checkArgument(number.doubleValue() >= 0, "Value must be non-negative.");
+  }
+
+  @Override
+  public String toString(Class<? extends Number> argType, Annotation 
annotation) {
+    return "must be >= 0";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/NotNull.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/NotNull.java 
b/commons/src/main/java/com/twitter/common/args/constraints/NotNull.java
new file mode 100644
index 0000000..1b425fb
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/NotNull.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation to indicate that an entity must be non-null.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface NotNull {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/NotNullVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/NotNullVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/NotNullVerifier.java
new file mode 100644
index 0000000..2d4e140
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/NotNullVerifier.java
@@ -0,0 +1,42 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * A verifier that ensures a value is non-null.
+ *
+ * @author William Farner
+ */
+@VerifierFor(NotNull.class)
+public class NotNullVerifier implements Verifier<Object> {
+  @Override
+  public void verify(Object value, Annotation annotation) {
+    checkArgument(value != null, "Value must not be null.");
+  }
+
+  @Override
+  public String toString(Class<?> argType, Annotation annotation) {
+    return "not null";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/Positive.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/Positive.java 
b/commons/src/main/java/com/twitter/common/args/constraints/Positive.java
new file mode 100644
index 0000000..a80bba6
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/Positive.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates an entity must be positive.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface Positive {
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/PositiveNumberVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/PositiveNumberVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/PositiveNumberVerifier.java
new file mode 100644
index 0000000..06bf3a3
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/PositiveNumberVerifier.java
@@ -0,0 +1,42 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Annotation;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifier to ensure that a number is positive.
+ *
+ * @author William Farner
+ */
+@VerifierFor(Positive.class)
+public class PositiveNumberVerifier implements Verifier<Number> {
+  @Override
+  public void verify(Number number, Annotation annotation) {
+    checkArgument(number.doubleValue() > 0, "Value must be positive.");
+  }
+
+  @Override
+  public String toString(Class<? extends Number> argType, Annotation 
annotation) {
+    return "must be > 0";
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/Range.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/Range.java 
b/commons/src/main/java/com/twitter/common/args/constraints/Range.java
new file mode 100644
index 0000000..ee4630d
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/constraints/Range.java
@@ -0,0 +1,43 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation that indicates a field must be within a given numeric range.
+ *
+ * @author William Farner
+ */
+@Target(FIELD)
+@Retention(RUNTIME)
+public @interface Range {
+
+  /**
+   * The lower bound on the acceptable range (inclusive).
+   */
+  double lower();
+
+  /**
+   * The upper bound on the acceptable range (inclusive).
+   */
+  double upper();
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/constraints/RangeNumberVerifier.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/constraints/RangeNumberVerifier.java
 
b/commons/src/main/java/com/twitter/common/args/constraints/RangeNumberVerifier.java
new file mode 100644
index 0000000..a4c2072
--- /dev/null
+++ 
b/commons/src/main/java/com/twitter/common/args/constraints/RangeNumberVerifier.java
@@ -0,0 +1,77 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.constraints;
+
+import java.lang.annotation.Annotation;
+import java.math.BigDecimal;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+
+import com.twitter.common.args.Verifier;
+import com.twitter.common.args.VerifierFor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Verifies that a number (inclusively) lies within a range.
+ *
+ * @author William Farner
+ */
+@VerifierFor(Range.class)
+public class RangeNumberVerifier implements Verifier<Number> {
+  @Override
+  public void verify(Number value, Annotation annotation) {
+    Range range = getRange(annotation);
+
+    checkArgument(range.lower() < range.upper(),
+        "Range lower bound must be greater than upper bound.");
+
+    double dblValue = value.doubleValue();
+    checkArgument(dblValue >= range.lower() && dblValue <= range.upper(),
+        String.format("Value must be in range [%f, %f]", range.lower(), 
range.upper()));
+  }
+
+  @Override
+  public String toString(Class<? extends Number> argType, Annotation 
annotation) {
+    Range range = getRange(annotation);
+
+    Function<Number, Number> converter;
+    if (Float.class.isAssignableFrom(argType)
+        || Double.class.isAssignableFrom(argType)
+        || BigDecimal.class.isAssignableFrom(argType)) {
+
+      converter = Functions.identity();
+    } else {
+      converter = new Function<Number, Number>() {
+        @Override public Number apply(Number item) {
+          return item.longValue();
+        }
+      };
+    }
+
+    return String.format("must be >= %s and <= %s",
+                         converter.apply(range.lower()),
+                         converter.apply(range.upper()));
+  }
+
+  private Range getRange(Annotation annotation) {
+    checkArgument(annotation instanceof Range, "Annotation is not a range: " + 
annotation);
+
+    return (Range) annotation;
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/parsers/AmountParser.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/parsers/AmountParser.java 
b/commons/src/main/java/com/twitter/common/args/parsers/AmountParser.java
new file mode 100644
index 0000000..1f4065d
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/parsers/AmountParser.java
@@ -0,0 +1,89 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.parsers;
+
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.google.common.reflect.TypeToken;
+
+import com.twitter.common.args.ArgParser;
+import com.twitter.common.args.Parser;
+import com.twitter.common.args.ParserOracle;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Unit;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Amount parser.
+ *
+ * @author William Farner
+ */
+@ArgParser
+public class AmountParser extends TypeParameterizedParser<Amount<?, ?>> {
+
+  private static final Pattern AMOUNT_PATTERN = 
Pattern.compile("(\\d+)([A-Za-z]+)");
+
+  public AmountParser() {
+    super(2);
+  }
+
+  @Override
+  Amount<?, ?> doParse(ParserOracle parserOracle, String raw, List<Type> 
typeParams) {
+    Type valueType = typeParams.get(0);
+    Parser<?> parser = parserOracle.get(TypeToken.of(valueType));
+
+    Matcher matcher = AMOUNT_PATTERN.matcher(raw);
+    checkArgument(matcher.matches(),
+        "Value '" + raw + "' must be of the format 1ns, 4mb, etc.");
+
+    Number number = (Number) parser.parse(parserOracle, valueType, 
matcher.group(1));
+    String unitRaw = matcher.group(2);
+
+    Type unitType = typeParams.get(1);
+    @SuppressWarnings("rawtypes")
+    Parser<Unit> unitParser = parserOracle.get(TypeToken.of(Unit.class));
+    @SuppressWarnings("rawtypes")
+    Unit unit = unitParser.parse(parserOracle, unitType, unitRaw);
+    checkArgument(unit.getClass() == unitType, String.format(
+        "Unit type (%s) does not match argument type (%s).",
+        unit.getClass(), unitType));
+
+    return create(valueType, number, unit);
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static Amount<?, ?> create(Type valueType, Number number, Unit unit) 
{
+    if (valueType == Integer.class) {
+      return Amount.of(number.intValue(), unit);
+    } else if (valueType == Double.class) {
+      return Amount.of(number.doubleValue(), unit);
+    } else if (valueType == Long.class) {
+      return Amount.of(number.longValue(), unit);
+    } else if (valueType == Byte.class) {
+      return Amount.of(number.byteValue(), unit);
+    } else if (valueType == Short.class) {
+      return Amount.of(number.shortValue(), unit);
+    } else if (valueType == Float.class) {
+      return Amount.of(number.floatValue(), unit);
+    }
+    throw new IllegalArgumentException("Unrecognized number class " + 
valueType);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/parsers/BooleanParser.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/parsers/BooleanParser.java 
b/commons/src/main/java/com/twitter/common/args/parsers/BooleanParser.java
new file mode 100644
index 0000000..7564d27
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/parsers/BooleanParser.java
@@ -0,0 +1,33 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.parsers;
+
+import com.twitter.common.args.ArgParser;
+
+/**
+ * Boolean parser.
+ *
+ * @author William Farner
+ */
+@ArgParser
+public class BooleanParser extends NonParameterizedTypeParser<Boolean> {
+  @Override
+  public Boolean doParse(String raw) {
+    // Magic boolean syntax, no argument value means true.
+    return raw.isEmpty() || Boolean.parseBoolean(raw);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/main/java/com/twitter/common/args/parsers/ByteParser.java
----------------------------------------------------------------------
diff --git 
a/commons/src/main/java/com/twitter/common/args/parsers/ByteParser.java 
b/commons/src/main/java/com/twitter/common/args/parsers/ByteParser.java
new file mode 100644
index 0000000..68b4823
--- /dev/null
+++ b/commons/src/main/java/com/twitter/common/args/parsers/ByteParser.java
@@ -0,0 +1,32 @@
+// 
=================================================================================================
+// Copyright 2011 Twitter, Inc.
+// 
-------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args.parsers;
+
+import com.twitter.common.args.ArgParser;
+
+/**
+ * Byte parser.
+ *
+ * @author William Farner
+ */
+@ArgParser
+public class ByteParser extends NumberParser<Byte> {
+  @Override
+  Byte parseNumber(String raw) {
+    return Byte.parseByte(raw);
+  }
+}

Reply via email to