You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@twill.apache.org by ch...@apache.org on 2013/12/13 23:27:36 UTC

[20/28] [TWILL-14] Bootstrapping for the site generation. Reorganization of the source tree happens:

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/api/TwillSpecification.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/api/TwillSpecification.java b/twill-api/src/main/java/org/apache/twill/api/TwillSpecification.java
new file mode 100644
index 0000000..00d171d
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/api/TwillSpecification.java
@@ -0,0 +1,327 @@
+/*
+ * 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 org.apache.twill.api;
+
+import org.apache.twill.internal.DefaultLocalFile;
+import org.apache.twill.internal.DefaultRuntimeSpecification;
+import org.apache.twill.internal.DefaultTwillRunnableSpecification;
+import org.apache.twill.internal.DefaultTwillSpecification;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.net.URI;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents specification of a {@link TwillApplication}.
+ */
+public interface TwillSpecification {
+
+  /**
+   * Defines execution order.
+   */
+  interface Order {
+
+    enum Type {
+      STARTED,
+      COMPLETED
+    }
+
+    /**
+     * @return Set of {@link TwillRunnable} name that belongs to this order.
+     */
+    Set<String> getNames();
+
+    Type getType();
+  }
+
+  /**
+   * @return Name of the application.
+   */
+  String getName();
+
+  /**
+   * @return A map from {@link TwillRunnable} name to {@link RuntimeSpecification}.
+   */
+  Map<String, RuntimeSpecification> getRunnables();
+
+  /**
+   * @return Returns a list of runnable names that should be executed in the given order.
+   */
+  List<Order> getOrders();
+
+  /**
+   * @return The {@link EventHandlerSpecification} for the {@link EventHandler} to be used for this application,
+   *         or {@code null} if no event handler has been provided.
+   */
+  @Nullable
+  EventHandlerSpecification getEventHandler();
+
+  /**
+   * Builder for constructing instance of {@link TwillSpecification}.
+   */
+  static final class Builder {
+
+    private String name;
+    private Map<String, RuntimeSpecification> runnables = Maps.newHashMap();
+    private List<Order> orders = Lists.newArrayList();
+    private EventHandlerSpecification eventHandler;
+
+    public static NameSetter with() {
+      return new Builder().new NameSetter();
+    }
+
+    public final class NameSetter {
+      public AfterName setName(String name) {
+        Builder.this.name = name;
+        return new AfterName();
+      }
+    }
+
+    public final class AfterName {
+      public MoreRunnable withRunnable() {
+        return new RunnableSetter();
+      }
+    }
+
+    public interface MoreRunnable {
+      RuntimeSpecificationAdder add(TwillRunnable runnable);
+
+      RuntimeSpecificationAdder add(TwillRunnable runnable, ResourceSpecification resourceSpec);
+
+      /**
+       * Adds a {@link TwillRunnable} with {@link ResourceSpecification#BASIC} resource specification.
+       * @param name Name of runnable
+       * @param runnable {@link TwillRunnable} to be run
+       * @return instance of {@link RuntimeSpecificationAdder}
+       */
+      RuntimeSpecificationAdder add(String name, TwillRunnable runnable);
+
+      RuntimeSpecificationAdder add(String name, TwillRunnable runnable, ResourceSpecification resourceSpec);
+    }
+
+    public interface AfterRunnable {
+      FirstOrder withOrder();
+
+      AfterOrder anyOrder();
+    }
+
+    public final class RunnableSetter implements MoreRunnable, AfterRunnable {
+
+      @Override
+      public RuntimeSpecificationAdder add(TwillRunnable runnable) {
+        return add(runnable.configure().getName(), runnable);
+      }
+
+      @Override
+      public RuntimeSpecificationAdder add(TwillRunnable runnable, ResourceSpecification resourceSpec) {
+        return add(runnable.configure().getName(), runnable, resourceSpec);
+      }
+
+      @Override
+      public RuntimeSpecificationAdder add(String name, TwillRunnable runnable) {
+        return add(name, runnable, ResourceSpecification.BASIC);
+      }
+
+      @Override
+      public RuntimeSpecificationAdder add(String name, TwillRunnable runnable,
+                                           final ResourceSpecification resourceSpec) {
+        final TwillRunnableSpecification spec = new DefaultTwillRunnableSpecification(
+                                            runnable.getClass().getName(), name, runnable.configure().getConfigs());
+        return new RuntimeSpecificationAdder(new Function<Collection<LocalFile>, RunnableSetter>() {
+          @Override
+          public RunnableSetter apply(Collection<LocalFile> files) {
+            runnables.put(spec.getName(), new DefaultRuntimeSpecification(spec.getName(), spec, resourceSpec, files));
+            return RunnableSetter.this;
+          }
+        });
+      }
+
+      @Override
+      public FirstOrder withOrder() {
+        return new OrderSetter();
+      }
+
+      @Override
+      public AfterOrder anyOrder() {
+        return new OrderSetter();
+      }
+    }
+
+    /**
+     * For setting runtime specific settings.
+     */
+    public final class RuntimeSpecificationAdder {
+
+      private final Function<Collection<LocalFile>, RunnableSetter> completer;
+
+      RuntimeSpecificationAdder(Function<Collection<LocalFile>, RunnableSetter> completer) {
+        this.completer = completer;
+      }
+
+      public LocalFileAdder withLocalFiles() {
+        return new MoreFile(completer);
+      }
+
+      public RunnableSetter noLocalFiles() {
+        return completer.apply(ImmutableList.<LocalFile>of());
+      }
+    }
+
+    public interface LocalFileAdder {
+      MoreFile add(String name, File file);
+
+      MoreFile add(String name, URI uri);
+
+      MoreFile add(String name, File file, boolean archive);
+
+      MoreFile add(String name, URI uri, boolean archive);
+
+      MoreFile add(String name, File file, String pattern);
+
+      MoreFile add(String name, URI uri, String pattern);
+    }
+
+    public final class MoreFile implements LocalFileAdder {
+
+      private final Function<Collection<LocalFile>, RunnableSetter> completer;
+      private final List<LocalFile> files = Lists.newArrayList();
+
+      public MoreFile(Function<Collection<LocalFile>, RunnableSetter> completer) {
+        this.completer = completer;
+      }
+
+      @Override
+      public MoreFile add(String name, File file) {
+        return add(name, file, false);
+      }
+
+      @Override
+      public MoreFile add(String name, URI uri) {
+        return add(name, uri, false);
+      }
+
+      @Override
+      public MoreFile add(String name, File file, boolean archive) {
+        return add(name, file.toURI(), archive);
+      }
+
+      @Override
+      public MoreFile add(String name, URI uri, boolean archive) {
+        files.add(new DefaultLocalFile(name, uri, -1, -1, archive, null));
+        return this;
+      }
+
+      @Override
+      public MoreFile add(String name, File file, String pattern) {
+        return add(name, file.toURI(), pattern);
+      }
+
+      @Override
+      public MoreFile add(String name, URI uri, String pattern) {
+        files.add(new DefaultLocalFile(name, uri, -1, -1, true, pattern));
+        return this;
+      }
+
+      public RunnableSetter apply() {
+        return completer.apply(files);
+      }
+    }
+
+    public interface FirstOrder {
+      NextOrder begin(String name, String...names);
+    }
+
+    public interface NextOrder extends AfterOrder {
+      NextOrder nextWhenStarted(String name, String...names);
+
+      NextOrder nextWhenCompleted(String name, String...names);
+    }
+
+    public interface AfterOrder {
+      AfterOrder withEventHandler(EventHandler handler);
+
+      TwillSpecification build();
+    }
+
+    public final class OrderSetter implements FirstOrder, NextOrder {
+      @Override
+      public NextOrder begin(String name, String... names) {
+        addOrder(Order.Type.STARTED, name, names);
+        return this;
+      }
+
+      @Override
+      public NextOrder nextWhenStarted(String name, String... names) {
+        addOrder(Order.Type.STARTED, name, names);
+        return this;
+      }
+
+      @Override
+      public NextOrder nextWhenCompleted(String name, String... names) {
+        addOrder(Order.Type.COMPLETED, name, names);
+        return this;
+      }
+
+      @Override
+      public AfterOrder withEventHandler(EventHandler handler) {
+        eventHandler = handler.configure();
+        return this;
+      }
+
+      @Override
+      public TwillSpecification build() {
+        // Set to track with runnable hasn't been assigned an order.
+        Set<String> runnableNames = Sets.newHashSet(runnables.keySet());
+        for (Order order : orders) {
+          runnableNames.removeAll(order.getNames());
+        }
+
+        // For all unordered runnables, add it to the end of orders list
+        orders.add(new DefaultTwillSpecification.DefaultOrder(runnableNames, Order.Type.STARTED));
+
+        return new DefaultTwillSpecification(name, runnables, orders, eventHandler);
+      }
+
+      private void addOrder(final Order.Type type, String name, String...names) {
+        Preconditions.checkArgument(name != null, "Name cannot be null.");
+        Preconditions.checkArgument(runnables.containsKey(name), "Runnable not exists.");
+
+        Set<String> runnableNames = Sets.newHashSet(name);
+        for (String runnableName : names) {
+          Preconditions.checkArgument(name != null, "Name cannot be null.");
+          Preconditions.checkArgument(runnables.containsKey(name), "Runnable not exists.");
+          runnableNames.add(runnableName);
+        }
+
+        orders.add(new DefaultTwillSpecification.DefaultOrder(runnableNames, type));
+      }
+    }
+
+    private Builder() {}
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/api/logging/LogEntry.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/api/logging/LogEntry.java b/twill-api/src/main/java/org/apache/twill/api/logging/LogEntry.java
new file mode 100644
index 0000000..4995328
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/api/logging/LogEntry.java
@@ -0,0 +1,58 @@
+/*
+ * 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 org.apache.twill.api.logging;
+
+/**
+ * Represents a log entry emitted by application.
+ */
+public interface LogEntry {
+
+  /**
+   * Log level.
+   */
+  enum Level {
+    FATAL,
+    ERROR,
+    WARN,
+    INFO,
+    DEBUG,
+    TRACE
+  }
+
+  String getLoggerName();
+
+  String getHost();
+
+  long getTimestamp();
+
+  Level getLogLevel();
+
+  String getSourceClassName();
+
+  String getSourceMethodName();
+
+  String getFileName();
+
+  int getLineNumber();
+
+  String getThreadName();
+
+  String getMessage();
+
+  StackTraceElement[] getStackTraces();
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/api/logging/LogHandler.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/api/logging/LogHandler.java b/twill-api/src/main/java/org/apache/twill/api/logging/LogHandler.java
new file mode 100644
index 0000000..afded19
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/api/logging/LogHandler.java
@@ -0,0 +1,26 @@
+/*
+ * 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 org.apache.twill.api.logging;
+
+/**
+ *
+ */
+public interface LogHandler {
+
+  void onLog(LogEntry logEntry);
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/api/logging/PrinterLogHandler.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/api/logging/PrinterLogHandler.java b/twill-api/src/main/java/org/apache/twill/api/logging/PrinterLogHandler.java
new file mode 100644
index 0000000..71a2bca
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/api/logging/PrinterLogHandler.java
@@ -0,0 +1,101 @@
+/*
+ * 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 org.apache.twill.api.logging;
+
+import com.google.common.base.Splitter;
+
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Formatter;
+import java.util.TimeZone;
+
+/**
+ * A {@link LogHandler} that prints the {@link LogEntry} through a {@link PrintWriter}.
+ */
+public final class PrinterLogHandler implements LogHandler {
+
+  private static final ThreadLocal<DateFormat> DATE_FORMAT = new ThreadLocal<DateFormat>() {
+    @Override
+    protected DateFormat initialValue() {
+      DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SSS'Z'");
+      format.setTimeZone(TimeZone.getTimeZone("UTC"));
+      return format;
+    }
+  };
+
+  private final PrintWriter writer;
+  private final Formatter formatter;
+
+  /**
+   * Creates a {@link PrinterLogHandler} which has {@link LogEntry} written to the given {@link PrintWriter}.
+   * @param writer The write that log entries will write to.
+   */
+  public PrinterLogHandler(PrintWriter writer) {
+    this.writer = writer;
+    this.formatter = new Formatter(writer);
+  }
+
+  @Override
+  public void onLog(LogEntry logEntry) {
+    String utc = timestampToUTC(logEntry.getTimestamp());
+
+    formatter.format("%s %-5s %s [%s] [%s] %s:%s(%s:%d) - %s\n",
+                     utc,
+                     logEntry.getLogLevel().name(),
+                     getShortenLoggerName(logEntry.getLoggerName()),
+                     logEntry.getHost(),
+                     logEntry.getThreadName(),
+                     getSimpleClassName(logEntry.getSourceClassName()),
+                     logEntry.getSourceMethodName(),
+                     logEntry.getFileName(),
+                     logEntry.getLineNumber(),
+                     logEntry.getMessage());
+    formatter.flush();
+
+    StackTraceElement[] stackTraces = logEntry.getStackTraces();
+    if (stackTraces != null) {
+      for (StackTraceElement stackTrace : stackTraces) {
+        writer.append("\tat ").append(stackTrace.toString());
+        writer.println();
+      }
+      writer.flush();
+    }
+  }
+
+  private String timestampToUTC(long timestamp) {
+    return DATE_FORMAT.get().format(new Date(timestamp));
+  }
+
+  private String getShortenLoggerName(String loggerName) {
+    StringBuilder builder = new StringBuilder();
+    String previous = null;
+    for (String part : Splitter.on('.').split(loggerName)) {
+      if (previous != null) {
+        builder.append(previous.charAt(0)).append('.');
+      }
+      previous = part;
+    }
+    return builder.append(previous).toString();
+  }
+
+  private String getSimpleClassName(String className) {
+    return className.substring(className.lastIndexOf('.') + 1);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/api/logging/package-info.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/api/logging/package-info.java b/twill-api/src/main/java/org/apache/twill/api/logging/package-info.java
new file mode 100644
index 0000000..e325c18
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/api/logging/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * This package contains class for handling logging events.
+ */
+package org.apache.twill.api.logging;

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/api/package-info.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/api/package-info.java b/twill-api/src/main/java/org/apache/twill/api/package-info.java
new file mode 100644
index 0000000..5d9df6b
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/api/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+/**
+ * Classes in this package provides core functionality of the Twill library.
+ */
+package org.apache.twill.api;

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/DefaultEventHandlerSpecification.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/DefaultEventHandlerSpecification.java b/twill-api/src/main/java/org/apache/twill/internal/DefaultEventHandlerSpecification.java
new file mode 100644
index 0000000..df21400
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/DefaultEventHandlerSpecification.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.EventHandlerSpecification;
+import org.apache.twill.api.EventHandler;
+import org.apache.twill.api.EventHandlerSpecification;
+import org.apache.twill.api.EventHandlerSpecification;
+import com.google.common.collect.ImmutableMap;
+import org.apache.twill.api.EventHandlerSpecification;
+
+import java.util.Map;
+
+/**
+ *
+ */
+public class DefaultEventHandlerSpecification implements EventHandlerSpecification {
+
+  private final String className;
+  private final Map<String, String> configs;
+
+  public DefaultEventHandlerSpecification(String className, Map<String, String> configs) {
+    this.className = className;
+    this.configs = configs;
+  }
+
+  public DefaultEventHandlerSpecification(EventHandler eventHandler) {
+    EventHandlerSpecification spec = eventHandler.configure();
+    this.className = eventHandler.getClass().getName();
+    this.configs = ImmutableMap.copyOf(spec.getConfigs());
+  }
+
+  @Override
+  public String getClassName() {
+    return className;
+  }
+
+  @Override
+  public Map<String, String> getConfigs() {
+    return configs;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/DefaultLocalFile.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/DefaultLocalFile.java b/twill-api/src/main/java/org/apache/twill/internal/DefaultLocalFile.java
new file mode 100644
index 0000000..e43c0c0
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/DefaultLocalFile.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.LocalFile;
+
+import javax.annotation.Nullable;
+import java.net.URI;
+
+/**
+ * A straightforward implementation of {@link LocalFile}.
+ */
+public final class DefaultLocalFile implements LocalFile {
+
+  private final String name;
+  private final URI uri;
+  private final long lastModified;
+  private final long size;
+  private final boolean archive;
+  private final String pattern;
+
+  public DefaultLocalFile(String name, URI uri, long lastModified,
+                          long size, boolean archive, @Nullable String pattern) {
+    this.name = name;
+    this.uri = uri;
+    this.lastModified = lastModified;
+    this.size = size;
+    this.archive = archive;
+    this.pattern = pattern;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public URI getURI() {
+    return uri;
+  }
+
+  @Override
+  public long getLastModified() {
+    return lastModified;
+  }
+
+  @Override
+  public long getSize() {
+    return size;
+  }
+
+  @Override
+  public boolean isArchive() {
+    return archive;
+  }
+
+  @Override
+  public String getPattern() {
+    return pattern;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/DefaultResourceReport.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/DefaultResourceReport.java b/twill-api/src/main/java/org/apache/twill/internal/DefaultResourceReport.java
new file mode 100644
index 0000000..c4c8a29
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/DefaultResourceReport.java
@@ -0,0 +1,123 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.ResourceReport;
+import org.apache.twill.api.TwillRunResources;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Implementation of {@link org.apache.twill.api.ResourceReport} with some
+ * additional methods for maintaining the report.
+ */
+public final class DefaultResourceReport implements ResourceReport {
+  private final SetMultimap<String, TwillRunResources> usedResources;
+  private final TwillRunResources appMasterResources;
+  private final String applicationId;
+
+  public DefaultResourceReport(String applicationId, TwillRunResources masterResources) {
+    this.applicationId = applicationId;
+    this.appMasterResources = masterResources;
+    this.usedResources = HashMultimap.create();
+  }
+
+  public DefaultResourceReport(String applicationId, TwillRunResources masterResources,
+                               Map<String, Collection<TwillRunResources>> resources) {
+    this.applicationId = applicationId;
+    this.appMasterResources = masterResources;
+    this.usedResources = HashMultimap.create();
+    for (Map.Entry<String, Collection<TwillRunResources>> entry : resources.entrySet()) {
+      this.usedResources.putAll(entry.getKey(), entry.getValue());
+    }
+  }
+
+  /**
+   * Add resources used by an instance of the runnable.
+   *
+   * @param runnableName name of runnable.
+   * @param resources resources to add.
+   */
+  public void addRunResources(String runnableName, TwillRunResources resources) {
+    usedResources.put(runnableName, resources);
+  }
+
+  /**
+   * Remove the resource corresponding to the given runnable and container.
+   *
+   * @param runnableName name of runnable.
+   * @param containerId container id of the runnable.
+   */
+  public void removeRunnableResources(String runnableName, String containerId) {
+    TwillRunResources toRemove = null;
+    // could be faster if usedResources was a Table, but that makes returning the
+    // report a little more complex, and this does not need to be terribly fast.
+    for (TwillRunResources resources : usedResources.get(runnableName)) {
+      if (resources.getContainerId().equals(containerId)) {
+        toRemove = resources;
+        break;
+      }
+    }
+    usedResources.remove(runnableName, toRemove);
+  }
+
+  /**
+   * Get all the run resources being used by all instances of the specified runnable.
+   *
+   * @param runnableName the runnable name.
+   * @return resources being used by all instances of the runnable.
+   */
+  @Override
+  public Collection<TwillRunResources> getRunnableResources(String runnableName) {
+    return usedResources.get(runnableName);
+  }
+
+  /**
+   * Get all the run resources being used across all runnables.
+   *
+   * @return all run resources used by all instances of all runnables.
+   */
+  @Override
+  public Map<String, Collection<TwillRunResources>> getResources() {
+    return Multimaps.unmodifiableSetMultimap(usedResources).asMap();
+  }
+
+  /**
+   * Get the resources application master is using.
+   *
+   * @return resources being used by the application master.
+   */
+  @Override
+  public TwillRunResources getAppMasterResources() {
+    return appMasterResources;
+  }
+
+  /**
+   * Get the id of the application master.
+   *
+   * @return id of the application master.
+   */
+  @Override
+  public String getApplicationId() {
+    return applicationId;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/DefaultResourceSpecification.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/DefaultResourceSpecification.java b/twill-api/src/main/java/org/apache/twill/internal/DefaultResourceSpecification.java
new file mode 100644
index 0000000..1327ce5
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/DefaultResourceSpecification.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.ResourceSpecification;
+
+/**
+ * Straightforward implementation of {@link org.apache.twill.api.ResourceSpecification}.
+ */
+public final class DefaultResourceSpecification implements ResourceSpecification {
+  private final int virtualCores;
+  private final int memorySize;
+  private final int instances;
+  private final int uplink;
+  private final int downlink;
+
+  public DefaultResourceSpecification(int virtualCores, int memorySize, int instances, int uplink, int downlink) {
+    this.virtualCores = virtualCores;
+    this.memorySize = memorySize;
+    this.instances = instances;
+    this.uplink = uplink;
+    this.downlink = downlink;
+  }
+
+  @Deprecated
+  @Override
+  public int getCores() {
+    return virtualCores;
+  }
+
+  @Override
+  public int getVirtualCores() {
+    return virtualCores;
+  }
+
+  @Override
+  public int getMemorySize() {
+    return memorySize;
+  }
+
+  @Override
+  public int getInstances() {
+    return instances;
+  }
+
+  @Override
+  public int getUplink() {
+    return uplink;
+  }
+
+  @Override
+  public int getDownlink() {
+    return downlink;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/DefaultRuntimeSpecification.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/DefaultRuntimeSpecification.java b/twill-api/src/main/java/org/apache/twill/internal/DefaultRuntimeSpecification.java
new file mode 100644
index 0000000..c4f496e
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/DefaultRuntimeSpecification.java
@@ -0,0 +1,67 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.LocalFile;
+import org.apache.twill.api.ResourceSpecification;
+import org.apache.twill.api.RuntimeSpecification;
+import org.apache.twill.api.TwillRunnableSpecification;
+import com.google.common.collect.ImmutableList;
+
+import java.util.Collection;
+
+/**
+ * Straightforward implementation of {@link RuntimeSpecification}.
+ */
+public final class DefaultRuntimeSpecification implements RuntimeSpecification {
+
+  private final String name;
+  private final TwillRunnableSpecification runnableSpec;
+  private final ResourceSpecification resourceSpec;
+  private final Collection<LocalFile> localFiles;
+
+  public DefaultRuntimeSpecification(String name,
+                                     TwillRunnableSpecification runnableSpec,
+                                     ResourceSpecification resourceSpec,
+                                     Collection<LocalFile> localFiles) {
+    this.name = name;
+    this.runnableSpec = runnableSpec;
+    this.resourceSpec = resourceSpec;
+    this.localFiles = ImmutableList.copyOf(localFiles);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public TwillRunnableSpecification getRunnableSpecification() {
+    return runnableSpec;
+  }
+
+  @Override
+  public ResourceSpecification getResourceSpecification() {
+    return resourceSpec;
+  }
+
+  @Override
+  public Collection<LocalFile> getLocalFiles() {
+    return localFiles;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillRunResources.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillRunResources.java b/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillRunResources.java
new file mode 100644
index 0000000..bd8f8f5
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillRunResources.java
@@ -0,0 +1,106 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.TwillRunResources;
+
+/**
+ *  Straightforward implementation of {@link org.apache.twill.api.TwillRunResources}.
+ */
+public class DefaultTwillRunResources implements TwillRunResources {
+  private final String containerId;
+  private final int instanceId;
+  private final int virtualCores;
+  private final int memoryMB;
+  private final String host;
+
+  public DefaultTwillRunResources(int instanceId, String containerId,
+                                  int cores, int memoryMB, String host) {
+    this.instanceId = instanceId;
+    this.containerId = containerId;
+    this.virtualCores = cores;
+    this.memoryMB = memoryMB;
+    this.host = host;
+  }
+
+  /**
+   * @return instance id of the runnable.
+   */
+  @Override
+  public int getInstanceId() {
+    return instanceId;
+  }
+
+  /**
+   * @return id of the container the runnable is running in.
+   */
+  @Override
+  public String getContainerId() {
+    return containerId;
+  }
+
+  /**
+   * @return number of cores the runnable is allowed to use.  YARN must be at least v2.1.0 and
+   *   it must be configured to use cgroups in order for this to be a reflection of truth.
+   */
+  @Override
+  public int getVirtualCores() {
+    return virtualCores;
+  }
+
+  /**
+   * @return amount of memory in MB the runnable is allowed to use.
+   */
+  @Override
+  public int getMemoryMB() {
+    return memoryMB;
+  }
+
+  /**
+   * @return the host the runnable is running on.
+   */
+  @Override
+  public String getHost() {
+    return host;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof TwillRunResources)) {
+      return false;
+    }
+    TwillRunResources other = (TwillRunResources) o;
+    return (instanceId == other.getInstanceId()) &&
+      containerId.equals(other.getContainerId()) &&
+      host.equals(other.getHost()) &&
+      (virtualCores == other.getVirtualCores()) &&
+      (memoryMB == other.getMemoryMB());
+  }
+
+  @Override
+  public int hashCode() {
+    int hash = 17;
+    hash = 31 *  hash + containerId.hashCode();
+    hash = 31 *  hash + host.hashCode();
+    hash = 31 *  hash + (int) (instanceId ^ (instanceId >>> 32));
+    hash = 31 *  hash + (int) (virtualCores ^ (virtualCores >>> 32));
+    hash = 31 *  hash + (int) (memoryMB ^ (memoryMB >>> 32));
+    return hash;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillRunnableSpecification.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillRunnableSpecification.java b/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillRunnableSpecification.java
new file mode 100644
index 0000000..14ea7f5
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillRunnableSpecification.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.TwillRunnableSpecification;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+/**
+ * Straightforward implementation of {@link org.apache.twill.api.TwillRunnableSpecification}.
+ */
+public final class DefaultTwillRunnableSpecification implements TwillRunnableSpecification {
+
+  private final String className;
+  private final String name;
+  private final Map<String, String> arguments;
+
+  public DefaultTwillRunnableSpecification(String className, String name, Map<String, String> arguments) {
+    this.className = className;
+    this.name = name;
+    this.arguments = ImmutableMap.copyOf(arguments);
+  }
+
+  public DefaultTwillRunnableSpecification(String className, TwillRunnableSpecification other) {
+    this.className = className;
+    this.name = other.getName();
+    this.arguments = ImmutableMap.copyOf(other.getConfigs());
+  }
+
+  @Override
+  public String getClassName() {
+    return className;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public Map<String, String> getConfigs() {
+    return arguments;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillSpecification.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillSpecification.java b/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillSpecification.java
new file mode 100644
index 0000000..6bb2b15
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/DefaultTwillSpecification.java
@@ -0,0 +1,103 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.EventHandlerSpecification;
+import org.apache.twill.api.RuntimeSpecification;
+import org.apache.twill.api.TwillSpecification;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Straightforward implementation of {@link org.apache.twill.api.TwillSpecification}.
+ */
+public final class DefaultTwillSpecification implements TwillSpecification {
+
+  private final String name;
+  private final Map<String, RuntimeSpecification> runnables;
+  private final List<Order> orders;
+  private final EventHandlerSpecification eventHandler;
+
+  public DefaultTwillSpecification(String name, Map<String, RuntimeSpecification> runnables,
+                                   List<Order> orders, EventHandlerSpecification eventHandler) {
+    this.name = name;
+    this.runnables = ImmutableMap.copyOf(runnables);
+    this.orders = ImmutableList.copyOf(orders);
+    this.eventHandler = eventHandler;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public Map<String, RuntimeSpecification> getRunnables() {
+    return runnables;
+  }
+
+  @Override
+  public List<Order> getOrders() {
+    return orders;
+  }
+
+  @Nullable
+  @Override
+  public EventHandlerSpecification getEventHandler() {
+    return eventHandler;
+  }
+
+  /**
+   * Straightforward implementation of {@link Order}.
+   */
+  public static final class DefaultOrder implements Order {
+
+    private final Set<String> names;
+    private final Type type;
+
+    public DefaultOrder(Iterable<String> names, Type type) {
+      this.names = ImmutableSet.copyOf(names);
+      this.type = type;
+    }
+
+    @Override
+    public Set<String> getNames() {
+      return names;
+    }
+
+    @Override
+    public Type getType() {
+      return type;
+    }
+
+    @Override
+    public String toString() {
+      return Objects.toStringHelper(this)
+        .add("names", names)
+        .add("type", type)
+        .toString();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/RunIds.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/RunIds.java b/twill-api/src/main/java/org/apache/twill/internal/RunIds.java
new file mode 100644
index 0000000..7249d81
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/RunIds.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.apache.twill.internal;
+
+import org.apache.twill.api.RunId;
+import com.google.common.base.Preconditions;
+
+import java.util.UUID;
+
+/**
+ * Factory class for creating instance of {@link org.apache.twill.api.RunId}.
+ */
+public final class RunIds {
+
+  public static RunId generate() {
+    return new RunIdImpl(UUID.randomUUID().toString());
+  }
+
+  public static RunId fromString(String str) {
+    return new RunIdImpl(str);
+  }
+
+  private RunIds() {
+  }
+
+  private static final class RunIdImpl implements RunId {
+
+    final String id;
+
+    private RunIdImpl(String id) {
+      Preconditions.checkArgument(id != null, "RunId cannot be null.");
+      this.id = id;
+    }
+
+    @Override
+    public String getId() {
+      return id;
+    }
+
+    @Override
+    public String toString() {
+      return getId();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+      if (other == null || !(other instanceof RunId)) {
+        return false;
+      }
+      return id.equals(((RunId)other).getId());
+    }
+
+    @Override
+    public int hashCode() {
+      return id.hashCode();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-api/src/main/java/org/apache/twill/internal/package-info.java
----------------------------------------------------------------------
diff --git a/twill-api/src/main/java/org/apache/twill/internal/package-info.java b/twill-api/src/main/java/org/apache/twill/internal/package-info.java
new file mode 100644
index 0000000..8af8362
--- /dev/null
+++ b/twill-api/src/main/java/org/apache/twill/internal/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * Internal classes for Twill API.
+ */
+package org.apache.twill.internal;

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/pom.xml
----------------------------------------------------------------------
diff --git a/twill-common/pom.xml b/twill-common/pom.xml
new file mode 100644
index 0000000..a4372f6
--- /dev/null
+++ b/twill-common/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>twill-parent</artifactId>
+        <groupId>org.apache.twill</groupId>
+        <version>0.1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>twill-common</artifactId>
+    <name>Twill common library</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/common/Cancellable.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/common/Cancellable.java b/twill-common/src/main/java/org/apache/twill/common/Cancellable.java
new file mode 100644
index 0000000..08f22d3
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/common/Cancellable.java
@@ -0,0 +1,29 @@
+/*
+ * 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 org.apache.twill.common;
+
+/**
+ * Something, usually a task, that can be cancelled. Cancellation is performed by the cancel method.
+ */
+public interface Cancellable {
+  /**
+   * Attempts to cancel execution of this task.
+   */
+  void cancel();
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/common/ServiceListenerAdapter.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/common/ServiceListenerAdapter.java b/twill-common/src/main/java/org/apache/twill/common/ServiceListenerAdapter.java
new file mode 100644
index 0000000..527ba7d
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/common/ServiceListenerAdapter.java
@@ -0,0 +1,50 @@
+/*
+ * 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 org.apache.twill.common;
+
+import com.google.common.util.concurrent.Service;
+
+/**
+ * An adapter for implementing {@link Service.Listener} with all method default to no-op.
+ */
+public abstract class ServiceListenerAdapter implements Service.Listener {
+  @Override
+  public void starting() {
+    // No-op
+  }
+
+  @Override
+  public void running() {
+    // No-op
+  }
+
+  @Override
+  public void stopping(Service.State from) {
+    // No-op
+  }
+
+  @Override
+  public void terminated(Service.State from) {
+    // No-op
+  }
+
+  @Override
+  public void failed(Service.State from, Throwable failure) {
+    // No-op
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/common/Services.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/common/Services.java b/twill-common/src/main/java/org/apache/twill/common/Services.java
new file mode 100644
index 0000000..7e294f0
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/common/Services.java
@@ -0,0 +1,140 @@
+/*
+ * 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 org.apache.twill.common;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.Service;
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Utility methods for help dealing with {@link Service}.
+ */
+public final class Services {
+
+  /**
+   * Starts a list of {@link Service} one by one. Starting of next Service is triggered from the callback listener
+   * thread of the previous Service.
+   *
+   * @param firstService First service to start.
+   * @param moreServices The rest services to start.
+   * @return A {@link ListenableFuture} that will be completed when all services are started, with the
+   *         result carries the completed {@link ListenableFuture} of each corresponding service in the
+   *         same order as they are passed to this method.
+   */
+  public static ListenableFuture<List<ListenableFuture<Service.State>>> chainStart(Service firstService,
+                                                                                   Service...moreServices) {
+    return doChain(true, firstService, moreServices);
+  }
+
+  /**
+   * Stops a list of {@link Service} one by one. It behaves the same as
+   * {@link #chainStart(com.google.common.util.concurrent.Service, com.google.common.util.concurrent.Service...)}
+   * except {@link com.google.common.util.concurrent.Service#stop()} is called instead of start.
+   *
+   * @param firstService First service to stop.
+   * @param moreServices The rest services to stop.
+   * @return A {@link ListenableFuture} that will be completed when all services are stopped.
+   * @see #chainStart(com.google.common.util.concurrent.Service, com.google.common.util.concurrent.Service...)
+   */
+  public static ListenableFuture<List<ListenableFuture<Service.State>>> chainStop(Service firstService,
+                                                                                  Service...moreServices) {
+    return doChain(false, firstService, moreServices);
+  }
+
+  /**
+   * Returns a {@link ListenableFuture} that will be completed when the given service is stopped. If the service
+   * stopped due to error, the failure cause would be reflected in the future.
+   *
+   * @param service The {@link Service} to block on.
+   * @return A {@link ListenableFuture} that will be completed when the service is stopped.
+   */
+  public static ListenableFuture<Service.State> getCompletionFuture(Service service) {
+    final SettableFuture<Service.State> resultFuture = SettableFuture.create();
+
+    service.addListener(new ServiceListenerAdapter() {
+      @Override
+      public void terminated(Service.State from) {
+        resultFuture.set(Service.State.TERMINATED);
+      }
+
+      @Override
+      public void failed(Service.State from, Throwable failure) {
+        resultFuture.setException(failure);
+      }
+    }, Threads.SAME_THREAD_EXECUTOR);
+
+    Service.State state = service.state();
+    if (state == Service.State.TERMINATED) {
+      return Futures.immediateFuture(state);
+    } else if (state == Service.State.FAILED) {
+      return Futures.immediateFailedFuture(new IllegalStateException("Service failed with unknown exception."));
+    }
+
+    return resultFuture;
+  }
+
+  /**
+   * Performs the actual logic of chain Service start/stop.
+   */
+  private static ListenableFuture<List<ListenableFuture<Service.State>>> doChain(boolean doStart,
+                                                                                 Service firstService,
+                                                                                 Service...moreServices) {
+    SettableFuture<List<ListenableFuture<Service.State>>> resultFuture = SettableFuture.create();
+    List<ListenableFuture<Service.State>> result = Lists.newArrayListWithCapacity(moreServices.length + 1);
+
+    ListenableFuture<Service.State> future = doStart ? firstService.start() : firstService.stop();
+    future.addListener(createChainListener(future, moreServices, new AtomicInteger(0), result, resultFuture, doStart),
+                       Threads.SAME_THREAD_EXECUTOR);
+    return resultFuture;
+  }
+
+  /**
+   * Returns a {@link Runnable} that can be used as a {@link ListenableFuture} listener to trigger
+   * further service action or completing the result future. Used by
+   * {@link #doChain(boolean, com.google.common.util.concurrent.Service, com.google.common.util.concurrent.Service...)}
+   */
+  private static Runnable createChainListener(final ListenableFuture<Service.State> future, final Service[] services,
+                                              final AtomicInteger idx,
+                                              final List<ListenableFuture<Service.State>> result,
+                                              final SettableFuture<List<ListenableFuture<Service.State>>> resultFuture,
+                                              final boolean doStart) {
+    return new Runnable() {
+
+      @Override
+      public void run() {
+        result.add(future);
+        int nextIdx = idx.getAndIncrement();
+        if (nextIdx == services.length) {
+          resultFuture.set(result);
+          return;
+        }
+        ListenableFuture<Service.State> actionFuture = doStart ? services[nextIdx].start() : services[nextIdx].stop();
+        actionFuture.addListener(createChainListener(actionFuture, services, idx, result, resultFuture, doStart),
+                                 Threads.SAME_THREAD_EXECUTOR);
+      }
+    };
+  }
+
+  private Services() {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/common/Threads.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/common/Threads.java b/twill-common/src/main/java/org/apache/twill/common/Threads.java
new file mode 100644
index 0000000..e33a677
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/common/Threads.java
@@ -0,0 +1,52 @@
+/*
+ * 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 org.apache.twill.common;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ *
+ */
+public final class Threads {
+
+  /**
+   * A executor that execute task from the submitter thread.
+   */
+  public static final Executor SAME_THREAD_EXECUTOR = MoreExecutors.sameThreadExecutor();
+
+  /**
+   * Handy method to create {@link ThreadFactory} that creates daemon threads with the given name format.
+   *
+   * @param nameFormat Name format for the thread names
+   * @return A {@link ThreadFactory}.
+   * @see ThreadFactoryBuilder
+   */
+  public static ThreadFactory createDaemonThreadFactory(String nameFormat) {
+    return new ThreadFactoryBuilder()
+      .setDaemon(true)
+      .setNameFormat(nameFormat)
+      .build();
+  }
+
+  private Threads() {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/filesystem/ForwardingLocationFactory.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/filesystem/ForwardingLocationFactory.java b/twill-common/src/main/java/org/apache/twill/filesystem/ForwardingLocationFactory.java
new file mode 100644
index 0000000..d25ea20
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/filesystem/ForwardingLocationFactory.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.apache.twill.filesystem;
+
+/**
+ *
+ */
+public abstract class ForwardingLocationFactory implements LocationFactory {
+
+  private final LocationFactory delegate;
+
+  protected ForwardingLocationFactory(LocationFactory delegate) {
+    this.delegate = delegate;
+  }
+
+  public LocationFactory getDelegate() {
+    return delegate;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/filesystem/LocalLocation.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/filesystem/LocalLocation.java b/twill-common/src/main/java/org/apache/twill/filesystem/LocalLocation.java
new file mode 100644
index 0000000..d107eac
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/filesystem/LocalLocation.java
@@ -0,0 +1,205 @@
+/*
+ * 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 org.apache.twill.filesystem;
+
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.UUID;
+
+/**
+ * A concrete implementation of {@link Location} for the Local filesystem.
+ */
+final class LocalLocation implements Location {
+  private final File file;
+
+  /**
+   * Constructs a LocalLocation.
+   *
+   * @param file to the file.
+   */
+  LocalLocation(File file) {
+    this.file = file;
+  }
+
+  /**
+   * Checks if the this location exists on local file system.
+   *
+   * @return true if found; false otherwise.
+   * @throws java.io.IOException
+   */
+  @Override
+  public boolean exists() throws IOException {
+    return file.exists();
+  }
+
+  /**
+   * @return An {@link java.io.InputStream} for this location on local filesystem.
+   * @throws IOException
+   */
+  @Override
+  public InputStream getInputStream() throws IOException {
+    File parent = file.getParentFile();
+    if (!parent.exists()) {
+      parent.mkdirs();
+    }
+    return new FileInputStream(file);
+  }
+
+  /**
+   * @return An {@link java.io.OutputStream} for this location on local filesystem.
+   * @throws IOException
+   */
+  @Override
+  public OutputStream getOutputStream() throws IOException {
+    File parent = file.getParentFile();
+    if (!parent.exists()) {
+      parent.mkdirs();
+    }
+    return new FileOutputStream(file);
+  }
+
+  /**
+   * Local location doesn't supports permission. It's the same as calling {@link #getOutputStream()}.
+   */
+  @Override
+  public OutputStream getOutputStream(String permission) throws IOException {
+    return getOutputStream();
+  }
+
+  /**
+   * @return Returns the name of the file or directory denoteed by this abstract pathname.
+   */
+  @Override
+  public String getName() {
+    return file.getName();
+  }
+
+  @Override
+  public boolean createNew() throws IOException {
+    return file.createNewFile();
+  }
+
+  /**
+   * Appends the child to the current {@link Location} on local filesystem.
+   * <p>
+   * Returns a new instance of Location.
+   * </p>
+   *
+   * @param child to be appended to this location.
+   * @return A new instance of {@link Location}
+   * @throws IOException
+   */
+  @Override
+  public Location append(String child) throws IOException {
+    return new LocalLocation(new File(file, child));
+  }
+
+  @Override
+  public Location getTempFile(String suffix) throws IOException {
+    return new LocalLocation(
+      new File(file.getAbsolutePath() + "." + UUID.randomUUID() + (suffix == null ? TEMP_FILE_SUFFIX : suffix)));
+  }
+
+  /**
+   * @return A {@link URI} for this location on local filesystem.
+   */
+  @Override
+  public URI toURI() {
+    return file.toURI();
+  }
+
+  /**
+   * Deletes the file or directory denoted by this abstract pathname. If this
+   * pathname denotes a directory, then the directory must be empty in order
+   * to be deleted.
+   *
+   * @return true if and only if the file or directory is successfully delete; false otherwise.
+   */
+  @Override
+  public boolean delete() throws IOException {
+    return file.delete();
+  }
+
+  @Override
+  public boolean delete(boolean recursive) throws IOException {
+    if (!recursive) {
+      return delete();
+    }
+
+    Deque<File> stack = Lists.newLinkedList();
+    stack.add(file);
+    while (!stack.isEmpty()) {
+      File f = stack.peekLast();
+      File[] files = f.listFiles();
+
+      if (files != null && files.length != 0) {
+        Collections.addAll(stack, files);
+      } else {
+        if (!f.delete()) {
+          return false;
+        }
+        stack.pollLast();
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public Location renameTo(Location destination) throws IOException {
+    // destination will always be of the same type as this location
+    boolean success = file.renameTo(((LocalLocation) destination).file);
+    if (success) {
+      return new LocalLocation(((LocalLocation) destination).file);
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * Creates the directory named by this abstract pathname, including any necessary
+   * but nonexistent parent directories.
+   *
+   * @return true if and only if the renaming succeeded; false otherwise
+   */
+  @Override
+  public boolean mkdirs() throws IOException {
+    return file.mkdirs();
+  }
+
+  /**
+   * @return Length of file.
+   */
+  @Override
+  public long length() throws IOException {
+    return file.length();
+  }
+
+  @Override
+  public long lastModified() {
+    return file.lastModified();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/filesystem/LocalLocationFactory.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/filesystem/LocalLocationFactory.java b/twill-common/src/main/java/org/apache/twill/filesystem/LocalLocationFactory.java
new file mode 100644
index 0000000..f44cd87
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/filesystem/LocalLocationFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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 org.apache.twill.filesystem;
+
+import java.io.File;
+import java.net.URI;
+
+/**
+ * A {@link LocationFactory} for creating local file {@link Location}.
+ */
+public final class LocalLocationFactory implements LocationFactory {
+
+  private final File basePath;
+
+  /**
+   * Constructs a LocalLocationFactory that Location created will be relative to system root.
+   */
+  public LocalLocationFactory() {
+    this(new File("/"));
+  }
+
+  public LocalLocationFactory(File basePath) {
+    this.basePath = basePath;
+  }
+
+  @Override
+  public Location create(String path) {
+    return new LocalLocation(new File(basePath, path));
+  }
+
+  @Override
+  public Location create(URI uri) {
+    if (uri.isAbsolute()) {
+      return new LocalLocation(new File(uri));
+    }
+    return new LocalLocation(new File(basePath, uri.getPath()));
+  }
+
+  @Override
+  public Location getHomeLocation() {
+    return new LocalLocation(new File(System.getProperty("user.home")));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/filesystem/Location.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/filesystem/Location.java b/twill-common/src/main/java/org/apache/twill/filesystem/Location.java
new file mode 100644
index 0000000..dee9546
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/filesystem/Location.java
@@ -0,0 +1,154 @@
+/*
+ * 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 org.apache.twill.filesystem;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+
+/**
+ * This interface defines the location and operations of a resource on the filesystem.
+ * <p>
+ * {@link Location} is agnostic to the type of file system the resource is on.
+ * </p>
+ */
+public interface Location {
+  /**
+   * Suffix added to every temp file name generated with {@link #getTempFile(String)}.
+   */
+  static final String TEMP_FILE_SUFFIX = ".tmp";
+
+  /**
+   * Checks if the this location exists.
+   *
+   * @return true if found; false otherwise.
+   * @throws IOException
+   */
+  boolean exists() throws IOException;
+
+  /**
+   * @return Returns the name of the file or directory denoteed by this abstract pathname.
+   */
+  String getName();
+
+  /**
+   * Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name
+   * does not yet exist.
+   * @return {@code true} if the file is successfully create, {@code false} otherwise.
+   * @throws IOException
+   */
+  boolean createNew() throws IOException;
+
+  /**
+   * @return An {@link java.io.InputStream} for this location.
+   * @throws IOException
+   */
+  InputStream getInputStream() throws IOException;
+
+  /**
+   * @return An {@link java.io.OutputStream} for this location.
+   * @throws IOException
+   */
+  OutputStream getOutputStream() throws IOException;
+
+  /**
+   * Creates an {@link OutputStream} for this location with the given permission. The actual permission supported
+   * depends on implementation.
+   *
+   * @param permission A POSIX permission string.
+   * @return An {@link OutputStream} for writing to this location.
+   * @throws IOException If failed to create the {@link OutputStream}.
+   */
+  OutputStream getOutputStream(String permission) throws IOException;
+
+  /**
+   * Appends the child to the current {@link Location}.
+   * <p>
+   * Returns a new instance of Location.
+   * </p>
+   *
+   * @param child to be appended to this location.
+   * @return A new instance of {@link Location}
+   * @throws IOException
+   */
+  Location append(String child) throws IOException;
+
+  /**
+   * Returns unique location for temporary file to be placed near this location.
+   * Allows all temp files to follow same pattern for easier management of them.
+   * @param suffix part of the file name to include in the temp file name
+   * @return location of the temp file
+   * @throws IOException
+   */
+  Location getTempFile(String suffix) throws IOException;
+
+  /**
+   * @return A {@link java.net.URI} for this location.
+   */
+  URI toURI();
+
+  /**
+   * Deletes the file or directory denoted by this abstract pathname. If this
+   * pathname denotes a directory, then the directory must be empty in order
+   * to be deleted.
+   *
+   * @return true if and only if the file or directory is successfully delete; false otherwise.
+   */
+  boolean delete() throws IOException;
+
+  /**
+   * Deletes the file or directory denoted by this abstract pathname. If this
+   * pathname denotes a directory and {@code recursive} is {@code true}, then content of the
+   * directory will be deleted recursively, otherwise the directory must be empty in order to be deleted.
+   * Note that when calling this method with {@code recursive = true} for a directory, any
+   * failure during deletion will have some entries inside the directory being deleted while some are not.
+   *
+   * @param recursive Indicate if recursively delete a directory. Ignored if the pathname represents a file.
+   * @return true if and only if the file or directory is successfully delete; false otherwise.
+   */
+  boolean delete(boolean recursive) throws IOException;
+
+  /**
+   * Moves the file or directory denoted by this abstract pathname.
+   *
+   * @param destination destination location
+   * @return new location if and only if the file or directory is successfully moved; null otherwise.
+   */
+  @Nullable
+  Location renameTo(Location destination) throws IOException;
+
+  /**
+   * Creates the directory named by this abstract pathname, including any necessary
+   * but nonexistent parent directories.
+   *
+   * @return true if and only if the renaming succeeded; false otherwise
+   */
+  boolean mkdirs() throws IOException;
+
+  /**
+   * @return Length of file.
+   */
+  long length() throws IOException;
+
+  /**
+   * @return Last modified time of file.
+   */
+  long lastModified() throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/filesystem/LocationFactories.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/filesystem/LocationFactories.java b/twill-common/src/main/java/org/apache/twill/filesystem/LocationFactories.java
new file mode 100644
index 0000000..751a632
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/filesystem/LocationFactories.java
@@ -0,0 +1,67 @@
+/*
+ * 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 org.apache.twill.filesystem;
+
+import com.google.common.base.Throwables;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * Providers helper methods for creating different {@link LocationFactory}.
+ */
+public final class LocationFactories {
+
+  /**
+   * Creates a {@link LocationFactory} that always applies the giving namespace prefix.
+   */
+  public static LocationFactory namespace(LocationFactory delegate, final String namespace) {
+    return new ForwardingLocationFactory(delegate) {
+      @Override
+      public Location create(String path) {
+        try {
+          Location base = getDelegate().create(namespace);
+          return base.append(path);
+        } catch (IOException e) {
+          throw Throwables.propagate(e);
+        }
+      }
+
+      @Override
+      public Location create(URI uri) {
+        if (uri.isAbsolute()) {
+          return getDelegate().create(uri);
+        }
+        try {
+          Location base = getDelegate().create(namespace);
+          return base.append(uri.getPath());
+        } catch (IOException e) {
+          throw Throwables.propagate(e);
+        }
+      }
+
+      @Override
+      public Location getHomeLocation() {
+        return getDelegate().getHomeLocation();
+      }
+    };
+  }
+
+  private LocationFactories() {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-twill/blob/4a1c943c/twill-common/src/main/java/org/apache/twill/filesystem/LocationFactory.java
----------------------------------------------------------------------
diff --git a/twill-common/src/main/java/org/apache/twill/filesystem/LocationFactory.java b/twill-common/src/main/java/org/apache/twill/filesystem/LocationFactory.java
new file mode 100644
index 0000000..f88d94d
--- /dev/null
+++ b/twill-common/src/main/java/org/apache/twill/filesystem/LocationFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.twill.filesystem;
+
+import java.net.URI;
+
+/**
+ * Factory for creating instance of {@link Location}.
+ */
+public interface LocationFactory {
+
+  /**
+   * Creates an instance of {@link Location} of the given path.
+   * @param path The path representing the location.
+   * @return An instance of {@link Location}.
+   */
+  Location create(String path);
+
+  /**
+   * Creates an instance of {@link Location} based on {@link java.net.URI} <code>uri</code>.
+   *
+   * @param uri to the resource on the filesystem.
+   * @return An instance of {@link Location}
+   */
+  Location create(URI uri);
+
+  /**
+   * Returns the home location.
+   */
+  Location getHomeLocation();
+}