Repository: docker-java/docker-java
Pull request #145 (2015-02-06 14:32:38)

Split dockerfile into convenience classes


Patch:
diff --git a/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java
index 468dac7e..0634651b 100644
--- a/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java
+++ b/src/main/java/com/github/dockerjava/core/command/BuildImageCmdImpl.java
@@ -24,6 +24,7 @@ import com.github.dockerjava.core.CompressArchiveUtil;
 import com.github.dockerjava.core.GoLangFileMatch;
 import com.github.dockerjava.core.GoLangFileMatchException;
 import com.github.dockerjava.core.GoLangMatchFileFilter;
+import com.github.dockerjava.core.dockerfile.Dockerfile;
 
 import static jersey.repackaged.com.google.common.base.Preconditions.*;
 
@@ -34,14 +35,8 @@ import static jersey.repackaged.com.google.common.base.Preconditions.*;
  */
 public class BuildImageCmdImpl extends AbstrDockerCmd<BuildImageCmd, BuildImageCmd.Response> implements BuildImageCmd {
 
-	private static final Pattern ADD_OR_COPY_PATTERN = Pattern
-			.compile("^(ADD|COPY)\\s+(.*)\\s+(.*)$");
-
-	private static final Pattern ENV_PATTERN = Pattern
-			.compile("^ENV\\s+(.*)\\s+(.*)$");
 
 	private InputStream tarInputStream = null;
-	private File tarFile = null;
 	private String tag;
 	private boolean noCache;
 	private boolean remove = true;
@@ -50,9 +45,12 @@ public class BuildImageCmdImpl extends AbstrDockerCmd<BuildImageCmd, BuildImageC
 	public BuildImageCmdImpl(BuildImageCmd.Exec exec, File dockerFolder) {
 		super(exec);
 		checkNotNull(dockerFolder, "dockerFolder is null");
-		tarFile = buildDockerFolderTar(dockerFolder);
+
 		try {
-			withTarInputStream(FileUtils.openInputStream(tarFile));
+			withTarInputStream(
+                            new Dockerfile(new File(dockerFolder, "Dockerfile"))
+                                          .parse()
+                                          .buildDockerFolderTar() );
 		} catch (IOException e) {
 			// we just created the file this should never happen.
 			throw new RuntimeException(e);
@@ -140,9 +138,6 @@ public class BuildImageCmdImpl extends AbstrDockerCmd<BuildImageCmd, BuildImageC
 	@Override
 	public void close() throws IOException {
 		super.close();
-		if (tarFile != null) {
-			FileUtils.deleteQuietly(tarFile);
-		}
 
 		tarInputStream.close();
 	}
@@ -156,201 +151,5 @@ public class BuildImageCmdImpl extends AbstrDockerCmd<BuildImageCmd, BuildImageC
 				.append(!remove ? "--rm=false " : "").toString();
 	}
 
-	protected File buildDockerFolderTar(File dockerFolder) {
-		checkArgument(dockerFolder.exists(),
-				"Path %s doesn't exist", dockerFolder);
-		checkArgument(dockerFolder.isDirectory(),
-				"Folder %s doesn't exist", dockerFolder);
-		checkState(new File(dockerFolder, "Dockerfile").exists(),
-				"Dockerfile doesn't exist in " + dockerFolder);
-
-		// ARCHIVE TAR
-		String archiveNameWithOutExtension = UUID.randomUUID().toString();
-
-		File dockerFolderTar = null;
-
-		try {
-			File dockerFile = new File(dockerFolder, "Dockerfile");
-			List<String> dockerFileContent = FileUtils.readLines(dockerFile);
-
-			if (dockerFileContent.size() <= 0) {
-				throw new DockerClientException(String.format(
-						"Dockerfile %s is empty", dockerFile));
-			}
-
-			List<String> ignores = new ArrayList<String>();
-			File dockerIgnoreFile = new File(dockerFolder, ".dockerignore");
-			if (dockerIgnoreFile.exists()) {
-				int lineNumber = 0;
-				List<String> dockerIgnoreFileContent = FileUtils
-						.readLines(dockerIgnoreFile);
-				for (String pattern : dockerIgnoreFileContent) {
-					lineNumber++;
-					pattern = pattern.trim();
-					if (pattern.isEmpty()) {
-						continue; // skip empty lines
-					}
-					pattern = FilenameUtils.normalize(pattern);
-					try {
-						// validate pattern and make sure we aren't excluding
-						// Dockerfile
-						if (GoLangFileMatch.match(pattern, "Dockerfile")) {
-							throw new DockerClientException(
-									String.format(
-											"Dockerfile is excluded by pattern '%s' on line %s in .dockerignore file",
-											pattern, lineNumber));
-						}
-						ignores.add(pattern);
-					} catch (GoLangFileMatchException e) {
-						throw new DockerClientException(
-								String.format(
-										"Invalid pattern '%s' on line %s in .dockerignore file",
-										pattern, lineNumber));
-					}
-				}
-			}
-			List<File> filesToAdd = new ArrayList<File>();
-			filesToAdd.add(dockerFile);
-
-			Map<String, String> environmentMap = new HashMap<String, String>();
-
-			int lineNumber = 0;
-
-			for (String cmd : dockerFileContent) {
-
-				lineNumber++;
-
-				if (cmd.trim().isEmpty() || cmd.startsWith("#"))
-					continue; // skip emtpy and commend lines
-
-				final Matcher envMatcher = ENV_PATTERN.matcher(cmd.trim());
-
-				if (envMatcher.find()) {
-					if (envMatcher.groupCount() != 2)
-						throw new DockerClientException(String.format(
-								"Wrong ENV format on line [%d]", lineNumber));
-
-					String variable = envMatcher.group(1).trim();
-
-					String value = envMatcher.group(2).trim();
-
-					environmentMap.put(variable, value);
-				}
-
-				final Matcher matcher = ADD_OR_COPY_PATTERN.matcher(cmd.trim());
-				if (matcher.find()) {
-					if (matcher.groupCount() != 3) {
-						throw new DockerClientException(String.format(
-								"Wrong ADD or COPY format on line [%d]",
-								lineNumber));
-					}
-
-					String extractedResource = matcher.group(2);
-
-					String resource = filterForEnvironmentVars(
-							extractedResource, environmentMap).trim();
-
-					if (isFileResource(resource)) {
-						File src = new File(resource);
-						if (!src.isAbsolute()) {
-							src = new File(dockerFolder, resource)
-									.getCanonicalFile();
-						} else {
-							throw new DockerClientException(String.format(
-									"Source file %s must be relative to %s",
-									src, dockerFolder));
-						}
-
-						// if (!src.exists()) {
-						// throw new DockerClientException(String.format(
-						// "Source file %s doesn't exist", src));
-						// }
-						if (src.isDirectory()) {
-							Collection<File> files = FileUtils.listFiles(src,
-									new GoLangMatchFileFilter(src, ignores),
-									TrueFileFilter.INSTANCE);
-							filesToAdd.addAll(files);
-						} else if (!src.exists()) {
-							filesToAdd.addAll(resolveWildcards(src, ignores));
-						} else if (!GoLangFileMatch.match(ignores,
-								CompressArchiveUtil.relativize(dockerFolder,
-										src))) {
-							filesToAdd.add(src);
-						} else {
-							throw new DockerClientException(
-									String.format(
-											"Source file %s is excluded by .dockerignore file",
-											src));
-						}
-					}
-				}
-			}
 
-			return CompressArchiveUtil.archiveTARFiles(dockerFolder,
-					filesToAdd, archiveNameWithOutExtension);
-		} catch (IOException ex) {
-			FileUtils.deleteQuietly(dockerFolderTar);
-			throw new DockerClientException(
-					"Error occurred while preparing Docker context folder.", ex);
-		}
-	}
-
-	private Collection<File> resolveWildcards(File file, List<String> ignores) {
-		List<File> filesToAdd = new ArrayList<File>();
-
-		File parent = file.getParentFile();
-		if (parent != null) {
-			if (parent.isDirectory()) {
-				Collection<File> files = FileUtils.listFiles(parent,
-						new GoLangMatchFileFilter(parent, ignores),
-						TrueFileFilter.INSTANCE);
-				filesToAdd.addAll(files);
-			} else {
-				filesToAdd.addAll(resolveWildcards(parent, ignores));
-			}
-		} else {
-			throw new DockerClientException(String.format(
-					"Source file %s doesn't exist", file));
-		}
-
-		return filesToAdd;
-	}
-
-	private String filterForEnvironmentVars(String extractedResource,
-			Map<String, String> environmentMap) {
-
-		if (environmentMap.size() > 0) {
-
-			String currentResourceContent = extractedResource;
-
-			for (Map.Entry<String, String> entry : environmentMap.entrySet()) {
-
-				String variable = entry.getKey();
-
-				String replacementValue = entry.getValue();
-
-				// handle: $VARIABLE case
-				currentResourceContent = currentResourceContent.replaceAll(
-						"\\$" + variable, replacementValue);
-
-				// handle ${VARIABLE} case
-				currentResourceContent = currentResourceContent.replaceAll(
-						"\\$\\{" + variable + "\\}", replacementValue);
-
-			}
-
-			return currentResourceContent;
-		} else
-			return extractedResource;
-	}
-
-	private static boolean isFileResource(String resource) {
-		URI uri;
-		try {
-			uri = new URI(resource);
-		} catch (URISyntaxException e) {
-			throw new RuntimeException(e);
-		}
-		return uri.getScheme() == null || "file".equals(uri.getScheme());
-	}
 }
diff --git a/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java
new file mode 100644
index 00000000..72c6c795
--- /dev/null
+++ b/src/main/java/com/github/dockerjava/core/dockerfile/Dockerfile.java
@@ -0,0 +1,282 @@
+package com.github.dockerjava.core.dockerfile;
+
+import com.github.dockerjava.api.DockerClientException;
+import com.github.dockerjava.core.CompressArchiveUtil;
+import com.github.dockerjava.core.GoLangFileMatch;
+import com.github.dockerjava.core.GoLangFileMatchException;
+import com.github.dockerjava.core.GoLangMatchFileFilter;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import jersey.repackaged.com.google.common.base.Function;
+import jersey.repackaged.com.google.common.base.Objects;
+import jersey.repackaged.com.google.common.base.Optional;
+import jersey.repackaged.com.google.common.base.Predicate;
+import jersey.repackaged.com.google.common.collect.Collections2;
+
+/**
+ * Parse a Dockerfile.
+ */
+public class Dockerfile {
+
+  public final File dockerFile;
+
+  public Dockerfile(File dockerFile) {
+
+    if (!dockerFile.exists()) {
+      throw new IllegalStateException(
+          String.format("Dockerfile %s does not exist", dockerFile.getAbsolutePath()));
+    }
+
+    if (!dockerFile.isFile()) {
+      throw new IllegalStateException(
+          String.format("Dockerfile %s is not a file", dockerFile.getAbsolutePath()));
+    }
+
+    this.dockerFile = dockerFile;
+
+  }
+
+  private static class LineTransformer
+      implements Function<String, Optional<? extends DockerfileStatement>> {
+
+    private int line = 0;
+
+    @Override
+    public Optional<? extends DockerfileStatement> apply(String input) {
+      try {
+        line++;
+        return DockerfileStatement.createFromLine(input);
+
+      } catch (Exception ex) {
+        throw new DockerClientException("Error on dockerfile line " + line);
+      }
+    }
+  }
+
+  /**
+   * Not needed in modern guava
+   */
+  private static class MissingOptionalFilter
+      implements Predicate<Optional<? extends DockerfileStatement>> {
+
+    @Override
+    public boolean apply(Optional<? extends DockerfileStatement> optional) {
+      return (optional.orNull() != null);
+    }
+  }
+
+  /**
+   * Not needed in modern guava
+   */
+  private static class OptionalItemTransformer
+      implements Function<Optional<? extends DockerfileStatement>, DockerfileStatement> {
+
+    @Override
+    public DockerfileStatement apply(Optional<? extends DockerfileStatement> optional) {
+      return optional.orNull();
+    }
+  }
+
+  public Collection<DockerfileStatement> getStatements() throws IOException {
+    Collection<String> dockerFileContent = FileUtils.readLines(dockerFile);
+
+    if (dockerFileContent.size() <= 0) {
+      throw new DockerClientException(String.format(
+          "Dockerfile %s is empty", dockerFile));
+    }
+
+    Collection<Optional<? extends DockerfileStatement>> optionals = Collections2
+        .transform(dockerFileContent, new LineTransformer());
+
+    // Modern guava would be done here,
+    // With simply return Optional.presentInstances( optionals );
+    //
+    // So this entire function could simply be
+    // return Optional.presentInstances( Collections2.transform( FileUtils.readLines(dockerFile), new LineTransformer() ) );
+    //
+    // Until the dawn of that day, do it manually
+
+    return Collections2.transform(Collections2.filter(optionals, new MissingOptionalFilter()),
+                                  new OptionalItemTransformer());
+
+  }
+
+  public List<String> getIgnores() throws IOException {
+    List<String> ignores = new ArrayList<String>();
+    File dockerIgnoreFile = new File(getDockerFolder(), ".dockerignore");
+    if (dockerIgnoreFile.exists()) {
+      int lineNumber = 0;
+      List<String> dockerIgnoreFileContent = FileUtils.readLines(dockerIgnoreFile);
+      for (String pattern : dockerIgnoreFileContent) {
+        lineNumber++;
+        pattern = pattern.trim();
+        if (pattern.isEmpty()) {
+          continue; // skip empty lines
+        }
+        pattern = FilenameUtils.normalize(pattern);
+        try {
+          // validate pattern and make sure we aren't excluding Dockerfile
+          if (GoLangFileMatch.match(pattern, "Dockerfile")) {
+            throw new DockerClientException(
+                String.format(
+                    "Dockerfile is excluded by pattern '%s' on line %s in .dockerignore file",
+                    pattern, lineNumber));
+          }
+          ignores.add(pattern);
+        } catch (GoLangFileMatchException e) {
+          throw new DockerClientException(String.format(
+              "Invalid pattern '%s' on line %s in .dockerignore file", pattern, lineNumber));
+        }
+      }
+    }
+    return ignores;
+  }
+
+
+  public ScannedResult parse() throws IOException {
+    return new ScannedResult();
+  }
+
+
+  public File getDockerFolder() {
+    return dockerFile.getParentFile();
+  }
+
+
+  /**
+   * Result of scanning / parsing a docker file.
+   */
+  public class ScannedResult {
+
+    final List<String> ignores;
+    final Map<String, String> environmentMap = new HashMap<String, String>();
+    final List<File> filesToAdd = new ArrayList<File>();
+
+    public InputStream buildDockerFolderTar() {
+
+      // ARCHIVE TAR
+      File dockerFolderTar = null;
+
+      try {
+        String archiveNameWithOutExtension = UUID.randomUUID().toString();
+
+        dockerFolderTar = CompressArchiveUtil.archiveTARFiles(getDockerFolder(),
+                                                              filesToAdd,
+                                                              archiveNameWithOutExtension);
+        return FileUtils.openInputStream(dockerFolderTar);
+
+      } catch (IOException ex) {
+        FileUtils.deleteQuietly(dockerFolderTar);
+        throw new DockerClientException(
+            "Error occurred while preparing Docker context folder.", ex);
+      }
+    }
+
+    @Override
+    public String toString() {
+      return Objects.toStringHelper(this)
+          .add("ignores", ignores)
+          .add("environmentMap", environmentMap)
+          .add("filesToAdd", filesToAdd)
+          .toString();
+    }
+
+    public ScannedResult() throws IOException {
+
+      ignores = getIgnores();
+      filesToAdd.add(dockerFile);
+
+      for (DockerfileStatement statement : getStatements()) {
+        if (statement instanceof DockerfileStatement.Env) {
+          processEnvStatement((DockerfileStatement.Env) statement);
+        } else if (statement instanceof DockerfileStatement.Add) {
+          processAddStatement((DockerfileStatement.Add) statement);
+        }
+      }
+    }
+
+    private void processAddStatement(DockerfileStatement.Add add) throws IOException {
+
+      add = add.transform(environmentMap);
+
+      if (add.isFileResource()) {
+
+        File dockerFolder = getDockerFolder();
+        String resource = add.source;
+
+        File src = new File(resource);
+        if (!src.isAbsolute()) {
+          src = new File(dockerFolder, resource)
+              .getCanonicalFile();
+        } else {
+          throw new DockerClientException(String.format(
+              "Source file %s must be relative to %s",
+              src, dockerFolder));
+        }
+
+        // if (!src.exists()) {
+        // throw new DockerClientException(String.format(
+        // "Source file %s doesn't exist", src));
+        // }
+        if (src.isDirectory()) {
+          Collection<File> files = FileUtils.listFiles(src,
+                                                       new GoLangMatchFileFilter(src, ignores),
+                                                       TrueFileFilter.INSTANCE);
+          filesToAdd.addAll(files);
+        } else if (!src.exists()) {
+          filesToAdd.addAll(resolveWildcards(src, ignores));
+        } else if (!GoLangFileMatch.match(ignores,
+                                          CompressArchiveUtil.relativize(dockerFolder,
+                                                                         src))) {
+          filesToAdd.add(src);
+        } else {
+          throw new DockerClientException(
+              String.format(
+                  "Source file %s is excluded by .dockerignore file",
+                  src));
+        }
+      }
+    }
+
+    private Collection<File> resolveWildcards(File file, List<String> ignores) {
+      List<File> filesToAdd = new ArrayList<File>();
+
+      File parent = file.getParentFile();
+      if (parent != null) {
+        if (parent.isDirectory()) {
+          Collection<File> files = FileUtils.listFiles(parent,
+                                                       new GoLangMatchFileFilter(parent, ignores),
+                                                       TrueFileFilter.INSTANCE);
+          filesToAdd.addAll(files);
+        } else {
+          filesToAdd.addAll(resolveWildcards(parent, ignores));
+        }
+      } else {
+        throw new DockerClientException(String.format(
+            "Source file %s doesn't exist", file));
+      }
+
+      return filesToAdd;
+    }
+
+    private void processEnvStatement(DockerfileStatement.Env env) {
+
+      environmentMap.put(env.variable, env.value);
+    }
+
+  }
+
+}
diff --git a/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java b/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java
new file mode 100644
index 00000000..1e2ddf1b
--- /dev/null
+++ b/src/main/java/com/github/dockerjava/core/dockerfile/DockerfileStatement.java
@@ -0,0 +1,208 @@
+package com.github.dockerjava.core.dockerfile;
+
+import com.github.dockerjava.api.DockerClientException;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jersey.repackaged.com.google.common.base.Objects;
+import jersey.repackaged.com.google.common.base.Optional;
+
+/**
+ * A statement present in a dockerfile.
+ */
+public abstract class DockerfileStatement<T extends DockerfileStatement> {
+
+  private DockerfileStatement() {
+  }
+
+  public T transform(Map<String, String> env) {
+    return (T) this;
+  }
+
+  protected String filterForEnvironmentVars(Map<String, String> environmentMap,
+                                            String extractedResource) {
+
+    if (environmentMap.size() > 0) {
+
+      String currentResourceContent = extractedResource;
+
+      for (Map.Entry<String, String> entry : environmentMap.entrySet()) {
+
+        String variable = entry.getKey();
+
+        String replacementValue = entry.getValue();
+
+        // handle: $VARIABLE case
+        currentResourceContent = currentResourceContent.replaceAll(
+            "\\$" + variable, replacementValue);
+
+        // handle ${VARIABLE} case
+        currentResourceContent = currentResourceContent.replaceAll(
+            "\\$\\{" + variable + "\\}", replacementValue);
+
+      }
+
+      return currentResourceContent;
+    } else {
+      return extractedResource;
+    }
+  }
+
+
+  /**
+   * A statement that we don't particularly care about.
+   */
+  public static class OtherLine extends DockerfileStatement {
+
+    public final String statement;
+
+    public OtherLine(String statement) {
+      this.statement = statement;
+    }
+
+    @Override
+    public String toString() {
+      return statement;
+    }
+  }
+
+  /**
+   * An ADD or a COPY
+   */
+  public static class Add extends DockerfileStatement<Add> {
+
+    private static final Pattern ADD_OR_COPY_PATTERN = Pattern
+        .compile("^(ADD|COPY)\\s+(.*)\\s+(.*)$");
+
+    public final String source;
+    public final String destination;
+
+    private Add(String source, String destination) {
+      this.source = source;
+      this.destination = destination;
+    }
+
+    private Add(final Matcher matcher) {
+      source = matcher.group(2);
+      destination = matcher.group(3);
+    }
+
+    @Override
+    public Add transform(Map<String, String> env) {
+      String resource = filterForEnvironmentVars(env, source).trim();
+      return new Add(resource, destination);
+    }
+
+    public boolean isFileResource() {
+      URI uri;
+      try {
+        uri = new URI(source);
+      } catch (URISyntaxException e) {
+        return false;
+      }
+      return uri.getScheme() == null || "file".equals(uri.getScheme());
+    }
+
+    /**
+     * Createa an Add if it matches, or missing if not.
+     *
+     * @param statement statement that may be an ADD or a COPY
+     * @return optional typed item.
+     */
+    public static Optional<Add> create(String statement) {
+      Matcher matcher = ADD_OR_COPY_PATTERN.matcher(statement.trim());
+      if (!matcher.find()) {
+        return Optional.absent();
+      }
+
+      if (matcher.groupCount() != 3) {
+        throw new DockerClientException("Wrong ADD or COPY format");
+      }
+
+      return Optional.of(new Add(matcher));
+    }
+
+
+    @Override
+    public String toString() {
+      return Objects.toStringHelper(this)
+          .add("source", source)
+          .add("destination", destination)
+          .toString();
+    }
+  }
+
+  public static class Env extends DockerfileStatement<Env> {
+
+    private static final Pattern ENV_PATTERN = Pattern
+        .compile("^ENV\\s+(.*)\\s+(.*)$");
+
+    public final String variable;
+    public final String value;
+
+    private Env(String variable, String value) {
+      this.variable = variable;
+      this.value = value;
+    }
+
+    private Env(Matcher envMatcher) {
+      this.variable = envMatcher.group(1).trim();
+      this.value = envMatcher.group(2).trim();
+    }
+
+    public static Optional<Env> create(String statement) {
+      Matcher matcher = ENV_PATTERN.matcher(statement.trim());
+      if (!matcher.find()) {
+        return Optional.absent();
+      }
+
+      if (matcher.groupCount() != 2) {
+        throw new DockerClientException("Wrong ENV format");
+      }
+
+      return Optional.of(new Env(matcher));
+    }
+
+    @Override
+    public String toString() {
+      return Objects.toStringHelper(this)
+          .add("variable", variable)
+          .add("value", value)
+          .toString();
+    }
+  }
+
+  /**
+   * Return a dockerfile statement
+   */
+  public static Optional<? extends DockerfileStatement> createFromLine(String cmd) {
+    if (cmd.trim().isEmpty() || cmd.startsWith("#")) {
+      return Optional.absent();
+    }
+
+    Optional<? extends DockerfileStatement> line;
+
+    line = Add.create(cmd);
+
+    // if (line.isPresent()) { : Newer guava
+    if (line.orNull() != null) {
+      return line;
+    }
+
+    line = Env.create(cmd);
+
+    // if (line.isPresent()) { : Newer guava
+    if (line.orNull() != null) {
+      return line;
+    }
+
+    return Optional.of(new OtherLine(cmd));
+
+
+  }
+
+}


Test patch:
diff --git a/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java b/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java
new file mode 100644
index 00000000..428511c2
--- /dev/null
+++ b/src/test/java/com/github/dockerjava/core/dockerfile/DockerfileTest.java
@@ -0,0 +1,50 @@
+package com.github.dockerjava.core.dockerfile;
+
+import junit.framework.TestCase;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DockerfileTest extends TestCase {
+
+  private static final Logger log = LoggerFactory.getLogger(DockerfileTest.class);
+
+  @Test
+  public void testAllItems() throws IOException {
+    File baseDir = new File(Thread.currentThread().getContextClassLoader()
+                                .getResource("netcat").getFile());
+
+    File root = baseDir.getParentFile();
+
+    Map<String, Dockerfile> dockerfiles = new HashMap<String, Dockerfile>();
+    Map<String, Dockerfile.ScannedResult> results = new HashMap<String, Dockerfile.ScannedResult>();
+
+    for (File child : root.listFiles()) {
+      if (new File(child, "Dockerfile").exists()) {
+        Dockerfile dockerfile = new Dockerfile(new File(child, "Dockerfile"));
+        dockerfiles.put(child.getName(), dockerfile);
+      }
+    }
+
+    for (String name : dockerfiles.keySet()) {
+      log.info("Scanning {}", name);
+      try {
+        results.put(name, dockerfiles.get(name).parse());
+      } catch (Exception ex) {
+        log.error("Error in {}", name, ex);
+      }
+
+    }
+
+    for (String name : results.keySet()) {
+      log.info("Name: {} = {}", name, results.get(name));
+    }
+  }
+
+}
\ No newline at end of file