WebAppCreator now has the ability to create apps from templates.
It can also mix different templates to generate the sample app based on the value passed with a
'-templates' parameter.

The sample app has now been separated into five templates:
- The sample code (sample template)
- Ant build.xml (ant template)
- Eclipse files (eclipse template)
- Maven pom.xml (maven template)
- The readme (readme template)
- The sample tests (_sample-test template)
- Eclipse test-related files (_eclipse-test template)

By default webAppCreator will use '-templates sample,ant,eclipse,readme'

The -junit parameter now causes test templates to be seached and added to the list of templates.
In this case, a template 'foo' will cause webAppCreator to attempt to also add a template '_foo-test'.

Legacy options (-maven, -noant, -XonlyEclipse, -XnoEclipse) are still honored, but are considered deprecated.

Future work will refactor tag definitions into each template.

Review at http://gwt-code-reviews.appspot.com/1371808


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9819 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/util/tools/Utility.java b/dev/core/src/com/google/gwt/util/tools/Utility.java
index f4b0a36..fddb1ac 100644
--- a/dev/core/src/com/google/gwt/util/tools/Utility.java
+++ b/dev/core/src/com/google/gwt/util/tools/Utility.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.LineNumberReader;
@@ -360,6 +361,13 @@
     return new String(hexString);
   }
 
+  public static void writeTemplateBinaryFile(File file, byte[] contents) throws IOException {
+
+    FileOutputStream o = new FileOutputStream(file);
+    o.write(contents);
+    close(o);
+  }
+
   public static void writeTemplateFile(File file, String contents,
       Map<String, String> replacements) throws IOException {
 
diff --git a/user/src/com/google/gwt/user/tools/WebAppCreator.java b/user/src/com/google/gwt/user/tools/WebAppCreator.java
index 7fb8ff2..8ddefe8 100644
--- a/user/src/com/google/gwt/user/tools/WebAppCreator.java
+++ b/user/src/com/google/gwt/user/tools/WebAppCreator.java
@@ -21,6 +21,7 @@
 import com.google.gwt.dev.DevMode;
 import com.google.gwt.dev.GwtVersion;
 import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.collect.HashSet;
 import com.google.gwt.user.tools.util.ArgHandlerIgnore;
 import com.google.gwt.user.tools.util.ArgHandlerOverwrite;
 import com.google.gwt.user.tools.util.CreatorUtilities;
@@ -31,13 +32,21 @@
 import com.google.gwt.util.tools.Utility;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 
 /**
  * Creates a GWT application skeleton.
@@ -56,6 +65,7 @@
     public ArgProcessor() {
       registerHandler(new ArgHandlerOverwriteExtension());
       registerHandler(new ArgHandlerIgnoreExtension());
+      registerHandler(new ArgHandlerTemplates());
       registerHandler(new ArgHandlerModuleName());
       registerHandler(new ArgHandlerOutDirExtension());
       registerHandler(new ArgHandlerNoEclipse());
@@ -83,99 +93,6 @@
     }
   }
 
-  private final class ArgHandlerModuleName extends ArgHandlerExtra {
-    @Override
-    public boolean addExtraArg(String arg) {
-      if (moduleName != null) {
-        System.err.println("Too many arguments.");
-        return false;
-      }
-
-      if (!CreatorUtilities.isValidModuleName(arg)) {
-        System.err.println("'"
-            + arg
-            + "' does not appear to be a valid fully-qualified Java class name.");
-        return false;
-      }
-
-      moduleName = arg;
-      return true;
-    }
-
-    @Override
-    public String getPurpose() {
-      return "The name of the module to create (e.g. com.example.myapp.MyApp)";
-    }
-
-    @Override
-    public String[] getTagArgs() {
-      return new String[] {"moduleName"};
-    }
-
-    @Override
-    public boolean isRequired() {
-      return true;
-    }
-  }
-
-  private final class ArgHandlerNoEclipse extends ArgHandlerFlag {
-    @Override
-    public String getPurpose() {
-      return "Do not generate eclipse files";
-    }
-
-    @Override
-    public String getTag() {
-      return "-XnoEclipse";
-    }
-
-    @Override
-    public boolean isUndocumented() {
-      return true;
-    }
-
-    @Override
-    public boolean setFlag() {
-      noEclipse = true;
-      return true;
-    }
-  }
-
-  private final class ArgHandlerOnlyEclipse extends ArgHandlerFlag {
-    @Override
-    public String getPurpose() {
-      return "Generate only eclipse files";
-    }
-
-    @Override
-    public String getTag() {
-      return "-XonlyEclipse";
-    }
-
-    @Override
-    public boolean isUndocumented() {
-      return true;
-    }
-
-    @Override
-    public boolean setFlag() {
-      onlyEclipse = true;
-      return true;
-    }
-  }
-
-  private final class ArgHandlerOverwriteExtension extends ArgHandlerOverwrite {
-    @Override
-    public boolean setFlag() {
-      if (ignore) {
-        System.err.println("-overwrite cannot be used with -ignore");
-        return false;
-      }
-      overwrite = true;
-      return true;
-    }
-  }
-
   private final class ArgHandlerJUnitPath extends ArgHandlerString {
 
     @Override
@@ -218,7 +135,8 @@
   private final class ArgHandlerMaven extends ArgHandlerFlag {
     @Override
     public String getPurpose() {
-      return "Create a maven2 project structure and pom file (default disabled)";
+      return "Deprecated. Create a maven2 project structure and pom file (default disabled). "
+          + "Equivalent to specifying 'maven' in the list of templates.";
     }
 
     @Override
@@ -228,7 +146,48 @@
 
     @Override
     public boolean setFlag() {
-      maven = true;
+      if (onlyEclipse) {
+        System.err.println("-maven and -XonlyEclipse cannot be used at the same time.");
+        return false;
+      }
+      if (!templates.contains("maven")) {
+        templates.add("maven");
+      }
+      return true;
+    }
+  }
+
+  private final class ArgHandlerModuleName extends ArgHandlerExtra {
+    @Override
+    public boolean addExtraArg(String arg) {
+      if (moduleName != null) {
+        System.err.println("Too many arguments.");
+        return false;
+      }
+
+      if (!CreatorUtilities.isValidModuleName(arg)) {
+        System.err.println("'"
+            + arg
+            + "' does not appear to be a valid fully-qualified Java class name.");
+        return false;
+      }
+
+      moduleName = arg;
+      return true;
+    }
+
+    @Override
+    public String getPurpose() {
+      return "The name of the module to create (e.g. com.example.myapp.MyApp)";
+    }
+
+    @Override
+    public String[] getTagArgs() {
+      return new String[] {"moduleName"};
+    }
+
+    @Override
+    public boolean isRequired() {
       return true;
     }
   }
@@ -236,7 +195,8 @@
   private final class ArgHandlerNoAnt extends ArgHandlerFlag {
     @Override
     public String getPurpose() {
-      return "Do not create an ant configuration file";
+      return "Deprecated. Do not create an ant configuration file. "
+          + "Equivalent to not specifying 'ant' in the list of templates.";
     }
 
     @Override
@@ -246,21 +206,179 @@
 
     @Override
     public boolean setFlag() {
-      ant = false;
+      argProcessingToDos.add(new Procrastinator() {
+        @Override
+        public void stopProcratinating() {
+          if (templates.contains("maven")) {
+            System.err.println("-maven and -noant are redundant. Continuing.");
+          }
+          if (templates.contains("ant")) {
+            System.err.println("Removing ant template from generated output.");
+            templates.remove("ant");
+          }
+        }
+      });
+      return true;
+    }
+  }
+
+  private final class ArgHandlerNoEclipse extends ArgHandlerFlag {
+    @Override
+    public String getPurpose() {
+      return "Deprecated. Do not generate eclipse files. "
+          + "Equivalent to not specifying 'eclipse' in the list of templates";
+    }
+
+    @Override
+    public String getTag() {
+      return "-XnoEclipse";
+    }
+
+    @Override
+    public boolean isUndocumented() {
+      return true;
+    }
+
+    @Override
+    public boolean setFlag() {
+      if (onlyEclipse) {
+        System.err.println("-XonlyEclipse and -XnoEclipse cannot be used at the same time.");
+        return false;
+      }
+      if (!templates.contains("maven")) {
+        System.err.println("-maven and -XnoEclipse are redundant. Continuing.");
+      }
+      noEclipse = true;
+      argProcessingToDos.add(new Procrastinator() {
+        @Override
+        public void stopProcratinating() {
+          if (templates.contains("eclipse")) {
+            System.err.println("Removing eclipse template from generated output.");
+            templates.remove("eclipse");
+          }
+        }
+      });
+      return true;
+    }
+  }
+
+  private final class ArgHandlerOnlyEclipse extends ArgHandlerFlag {
+    @Override
+    public String getPurpose() {
+      return "Deprecated. Generate only eclipse files. "
+          + "Equivalent to only specifying 'eclipse' in the list of templates.";
+    }
+
+    @Override
+    public String getTag() {
+      return "-XonlyEclipse";
+    }
+
+    @Override
+    public boolean isUndocumented() {
+      return true;
+    }
+
+    @Override
+    public boolean setFlag() {
+      if (noEclipse) {
+        System.err.println("-XonlyEclipse and -XnoEclipse cannot be used at the same time.");
+        return false;
+      }
+      if (templates.contains("maven")) {
+        System.err.println("-maven and -XonlyEclipse cannot be used at the same time.");
+        return false;
+      }
+      onlyEclipse = true;
+      argProcessingToDos.add(new Procrastinator() {
+        @Override
+        public void stopProcratinating() {
+          System.err.println("Removing all templates but 'eclipse' from generated output.");
+          templates.clear();
+          templates.add("eclipse");
+        }
+      });
+      return true;
+    }
+  }
+
+  private final class ArgHandlerOverwriteExtension extends ArgHandlerOverwrite {
+    @Override
+    public boolean setFlag() {
+      if (ignore) {
+        System.err.println("-overwrite cannot be used with -ignore");
+        return false;
+      }
+      overwrite = true;
+      return true;
+    }
+  }
+
+  private final class ArgHandlerTemplates extends ArgHandlerString {
+
+    @Override
+    public String[] getDefaultArgs() {
+      return new String[] {getTag(), "sample, ant, eclipse, readme"};
+    }
+
+    @Override
+    public String getPurpose() {
+      return "Specifies the template(s) to use (comma separeted)."
+          + " Defaults to 'sample,ant,eclipse,readme'";
+    }
+
+    @Override
+    public String getTag() {
+      return "-templates";
+    }
+
+    @Override
+    public String[] getTagArgs() {
+      return new String[] {"template1,template2,..."};
+    }
+
+    @Override
+    public boolean isRequired() {
+      return false;
+    }
+
+    @Override
+    public boolean setString(String str) {
+      String[] templateList = str.split(", *");
+      for (String template : templateList) {
+        URL url = getTemplateRoot(template);
+        if (url == null) {
+          System.err.println("Template not found: " + template);
+          return false;
+        }
+        templates.add(template);
+      }
       return true;
     }
   }
 
   private static final class FileCreator {
     private final File destDir;
-    private final String destName;
-    private final String sourceName;
 
-    public FileCreator(File destDir, String destName, String sourceName) {
+    private final String destName;
+    private final boolean isBinary;
+    private final String sourceName;
+    public FileCreator(File destDir, String destName, String sourceName, boolean isBinary) {
       this.destDir = destDir;
       this.sourceName = sourceName;
       this.destName = destName;
+      this.isBinary = isBinary;
     }
+
+    @Override
+    public String toString() {
+      return "FileCreator [destDir=" + destDir + ", destName=" + destName
+          + ", sourceName=" + sourceName + ", isBinary=" + isBinary + "]";
+    }
+  }
+
+  private abstract static class Procrastinator {
+    public abstract void stopProcratinating();
   }
 
   public static void main(String[] args) {
@@ -276,35 +394,64 @@
     return false;
   }
 
-  private boolean ant = true;
+  private static String getTemplateBasePath(String template) {
+    return "/" + WebAppCreator.class.getPackage().getName().replace('.', '/') + "/templates/" 
+        + template + "/";
+  }
+
+  private static URL getTemplateRoot(String template) {
+    return WebAppCreator.class.getResource("templates/" + template);
+  }
+
+  private static String replaceFileName(Map<String, String> replacements, String name) {
+    String replacedContents = name;
+    Set<Entry<String, String>> entries = replacements.entrySet();
+    for (Iterator<Entry<String, String>> iter = entries.iterator(); iter.hasNext();) {
+      Entry<String, String> entry = iter.next();
+      String replaceThis = entry.getKey();
+      replaceThis = replaceThis.replaceAll("@(.*)", "_$1_");
+      String withThis = entry.getValue();
+      withThis = withThis.replaceAll("\\\\", "\\\\\\\\");
+      withThis = withThis.replaceAll("\\$", "\\\\\\$");
+      replacedContents = replacedContents.replaceAll(replaceThis, withThis);
+    }
+    return replacedContents;
+  }
+  private ArrayList<Procrastinator> argProcessingToDos = new ArrayList<Procrastinator>();
   private boolean ignore = false;
-  private boolean maven = false;
+  private String junitPath = null;
   private String moduleName;
   private boolean noEclipse;
   private boolean onlyEclipse;
   private File outDir;
   private boolean overwrite = false;
-  private String junitPath = null;
 
-  /**
-   * Create the sample app.
-   * 
-   * @throws IOException if any disk write fails
-   * @deprecated as of GWT 2.1, replaced by {@link #doRun(String)}
-   */
-  @Deprecated
-  protected void doRun() throws IOException {
-    doRun(Utility.getInstallPath());
+  private HashSet<String> templates = new HashSet<String>();
+
+  public List<FileCreator> getFiles(Map<String, String> replacements)
+      throws IOException, WebAppCreatorException {
+    List<FileCreator> files = new ArrayList<FileCreator>();
+
+    Utility.getDirectory(outDir.getPath(), true);
+
+    for (String template : templates) {
+      URL templateUrl = getTemplateRoot(template);
+      if ("jar".equals(templateUrl.getProtocol())) {
+        files.addAll(getTemplateFilesFromZip(replacements, templateUrl, outDir));
+      } else if ("file".equals(templateUrl.getProtocol())) {
+        File templateRoot = new File(templateUrl.getPath());
+        files.addAll(getTemplateFiles(replacements, templateRoot, outDir,
+            getTemplateBasePath(template)));
+      } else {
+        throw new WebAppCreatorException("Cannot handle template '" + template + "' protocol: " 
+            + templateUrl.getProtocol());
+      }
+    }
+    
+    return files;
   }
 
-  /**
-   * Create the sample app.
-   * 
-   * @param installPath directory containing GWT libraries
-   * @throws IOException if any disk write fails
-   */
-  protected void doRun(String installPath) throws IOException {
-
+  public Map<String, String> getReplacements(String installPath, String theModuleName) {
     // GWT libraries
     String gwtUserPath = installPath + '/' + "gwt-user.jar";
     String gwtDevPath = installPath + '/' + "gwt-dev.jar";
@@ -323,37 +470,54 @@
     }
 
     // Compute module package and name.
-    int pos = moduleName.lastIndexOf('.');
-    String modulePackageName = moduleName.substring(0, pos);
-    String moduleShortName = moduleName.substring(pos + 1);
-
-    // pro-actively let ant user know that this script can also create tests.
-    if (junitPath == null && !maven) {
-      System.err.println("Not creating tests because -junit argument was not specified.\n");
-    }
-
-    // Compute module name and directories
-    String srcFolder = maven ? "src/main/java" : "src";
-    String testFolder = maven ? "src/test/java" : "test";
-    String warFolder = maven ? "src/main/webapp" : "war";
-    File srcDir = Utility.getDirectory(outDir, srcFolder, true);
-    File warDir = Utility.getDirectory(outDir, warFolder, true);
-    File webInfDir = Utility.getDirectory(warDir, "WEB-INF", true);
-    File libDir = Utility.getDirectory(webInfDir, "lib", true);
-    File moduleDir = Utility.getDirectory(srcDir, modulePackageName.replace(
-        '.', '/'), true);
-    File clientDir = Utility.getDirectory(moduleDir, "client", true);
-    File serverDir = Utility.getDirectory(moduleDir, "server", true);
-    File sharedDir = Utility.getDirectory(moduleDir, "shared", true);
-    File moduleTestDir = Utility.getDirectory(outDir, testFolder + "/"
-        + modulePackageName.replace('.', '/'), true);
-    File clientTestDir = Utility.getDirectory(moduleTestDir, "client", true);
+    int pos = theModuleName.lastIndexOf('.');
+    String modulePackageName = theModuleName.substring(0, pos);
+    String moduleShortName = theModuleName.substring(pos + 1);
 
     // Create a map of replacements
     Map<String, String> replacements = new HashMap<String, String>();
+
+    // Compute module name and directories
+    String srcFolder = templates.contains("maven") ? "src/main/java" : "src";
+    String testFolder = templates.contains("maven") ? "src/test/java" : "test";
+    String warFolder = templates.contains("maven") ? "src/main/webapp" : "war";
+
+    {
+      // pro-actively let ant user know that this script can also create tests.
+      if (junitPath == null) {
+        System.err.println("Not creating tests because -junit argument was not specified.\n");
+      }
+
+      String testTargetsBegin = "";
+      String testTargetsEnd = "";
+      String junitJarPath = junitPath;
+      String eclipseTestDir = "";
+      if (junitPath != null) {
+        eclipseTestDir = "\n   <classpathentry kind=\"src\" path=\""
+            + testFolder + "\"/>";
+      }
+      if (junitPath == null) {
+        testTargetsBegin = "\n<!--"
+            + "\n"
+            + "Test targets suppressed because -junit argument was not specified when running"
+            + " webAppCreator.\n";
+        testTargetsEnd = "-->\n";
+        junitJarPath = "path_to_the_junit_jar";
+      }
+      replacements.put("@testTargetsBegin", testTargetsBegin);
+      replacements.put("@testTargetsEnd", testTargetsEnd);
+      replacements.put("@junitJar", junitJarPath);
+      replacements.put("@eclipseTestDir", eclipseTestDir);
+    }
+
+    replacements.put("@srcFolder", srcFolder);
+    replacements.put("@testFolder", testFolder);
+    replacements.put("@warFolder", warFolder);
+
     replacements.put("@moduleShortName", moduleShortName);
     replacements.put("@modulePackageName", modulePackageName);
-    replacements.put("@moduleName", moduleName);
+    replacements.put("@moduleFolder", modulePackageName.replace('.', '/'));
+    replacements.put("@moduleName", theModuleName);
     replacements.put("@clientPackage", modulePackageName + ".client");
     replacements.put("@serverPackage", modulePackageName + ".server");
     replacements.put("@sharedPackage", modulePackageName + ".shared");
@@ -368,24 +532,20 @@
     replacements.put("@compileClass", Compiler.class.getName());
     replacements.put("@startupUrl", moduleShortName + ".html");
     replacements.put("@renameTo", moduleShortName.toLowerCase());
-    replacements.put("@moduleNameJUnit", moduleName + "JUnit");
-    replacements.put("@srcFolder", srcFolder);
-    replacements.put("@testFolder", testFolder);
-    replacements.put("@warFolder", warFolder);
+    replacements.put("@moduleNameJUnit", theModuleName + "JUnit");
 
     // Add command to copy gwt-servlet-deps.jar into libs, unless this is a
     // maven project. Maven projects should include libs as maven dependencies.
     String copyServletDeps = "";
-    if (!maven) {
-      copyServletDeps = "<copy todir=\"" + warFolder + "/WEB-INF/lib\" "
-          + "file=\"${gwt.sdk}/gwt-servlet-deps.jar\" />";
-    }
+    copyServletDeps = "<copy todir=\"" + warFolder + "/WEB-INF/lib\" "
+        + "file=\"${gwt.sdk}/gwt-servlet-deps.jar\" />";
     replacements.put("@copyServletDeps", copyServletDeps);
 
     // Collect the list of server libs to include on the eclipse classpath.
+    File libDirectory = new File(outDir + warFolder + "WEB-INF/lib");
     StringBuilder serverLibs = new StringBuilder();
-    if (libDir.exists()) {
-      for (File file : libDir.listFiles()) {
+    if (libDirectory.exists()) {
+      for (File file : libDirectory.listFiles()) {
         if (file.getName().toLowerCase().endsWith(".jar")) {
           serverLibs.append("   <classpathentry kind=\"lib\" path=\"war/WEB-INF/lib/");
           serverLibs.append(file.getName());
@@ -396,103 +556,103 @@
     replacements.put("@serverClasspathLibs", serverLibs.toString());
 
     String antEclipseRule = "";
-    if (noEclipse) {
+    if (!templates.contains("eclipse")) {
       /*
        * Generate a rule into the build file that allows for the generation of
        * an eclipse project later on. This is primarily for distro samples. This
        * is a quick and dirty way to inject a build rule, but it works.
        */
       antEclipseRule = "\n\n"
-          + "  <target name=\"eclipse.generate\" depends=\"libs\" description=\"Generate eclipse project\">\n"
+          + "  <target name=\"eclipse.generate\" depends=\"libs\" description"
+          + "=\"Generate eclipse project\">\n"
           + "    <java failonerror=\"true\" fork=\"true\" classname=\""
           + this.getClass().getName() + "\">\n" + "      <classpath>\n"
           + "        <path refid=\"project.class.path\"/>\n"
           + "      </classpath>\n" + "      <arg value=\"-XonlyEclipse\"/>\n"
           + "      <arg value=\"-ignore\"/>\n" + "      <arg value=\""
-          + moduleName + "\"/>\n" + "    </java>\n" + "  </target>";
+          + theModuleName + "\"/>\n" + "    </java>\n" + "  </target>";
     } else {
       antEclipseRule = "";
     }
     replacements.put("@antEclipseRule", antEclipseRule);
+    return replacements;
+  }
 
-    {
-      String testTargetsBegin = "";
-      String testTargetsEnd = "";
-      String junitJarPath = junitPath;
-      String eclipseTestDir = "";
-      if (junitPath != null || maven) {
-        eclipseTestDir = "\n   <classpathentry kind=\"src\" path=\""
-            + testFolder + "\"/>";
-      } 
-      if (junitPath == null) {
-        testTargetsBegin = "\n<!--"
-            + "\n"
-            + "Test targets suppressed because -junit argument was not specified when running webAppCreator.\n";
-        testTargetsEnd = "-->\n";
-        junitJarPath = "path_to_the_junit_jar";
-      }
-      replacements.put("@testTargetsBegin", testTargetsBegin);
-      replacements.put("@testTargetsEnd", testTargetsEnd);
-      replacements.put("@junitJar", junitJarPath);
-      replacements.put("@eclipseTestDir", eclipseTestDir);
+  /**
+   * Create the sample app.
+   * 
+   * @throws IOException if any disk write fails
+   * @throws WebAppCreatorException if any tag expansion of template processing fails
+   * @deprecated as of GWT 2.1, replaced by {@link #doRun(String)}
+   */
+  @Deprecated
+  protected void doRun() throws IOException, WebAppCreatorException {
+    doRun(Utility.getInstallPath());
+  }
+
+  /**
+   * Create the sample app.
+   * 
+   * @param installPath directory containing GWT libraries
+   * @throws IOException if any disk write fails
+   * @throws WebAppCreatorException  if any tag expansion of template processing fails
+   */
+  protected void doRun(String installPath) throws IOException, WebAppCreatorException {
+    for (Procrastinator toDo : argProcessingToDos) {
+      toDo.stopProcratinating();
     }
 
+    // Maven projects do not need Ant nor Eclipse files
+    if (templates.contains("maven")) {
+      junitPath = "junit-provided-by-maven";
+
+      if (templates.contains("eclipse")) {
+        System.err.println("'maven' template is being generated removing 'eclipse'"
+            + " template from generated output.");
+        templates.remove("eclipse");            
+      }
+      if (templates.contains("ant")) {
+        System.err.println("'maven' template is being generated removing 'ant'"
+            + " template from generated output.");
+        templates.remove("ant");
+      }
+    }
+
+    // Eagerly look for test templates
+    if (junitPath != null) {
+      ArrayList<String> testTemplates = new ArrayList<String>();
+      for (String template : templates) {
+        String testTemplateName = "_" + template + "-test";
+        if (getTemplateRoot(testTemplateName) != null) {
+          testTemplates.add(testTemplateName);
+        }
+      }
+      templates.addAll(testTemplates);
+    }
+
+    System.out.println("Generating from templates: " + templates);
+
+    // Generate string replacements 
+    Map<String, String> replacements = getReplacements(installPath, moduleName);
+
     // Create a list with the files to create
-    List<FileCreator> files = new ArrayList<FileCreator>();
-    if (!onlyEclipse) {
-      files.add(new FileCreator(moduleDir, moduleShortName + ".gwt.xml",
-          "Module.gwt.xml"));
-      files.add(new FileCreator(warDir, moduleShortName + ".html",
-          "AppHtml.html"));
-      files.add(new FileCreator(warDir, moduleShortName + ".css", "AppCss.css"));
-      files.add(new FileCreator(webInfDir, "web.xml", "web.xml"));
-      files.add(new FileCreator(clientDir, moduleShortName + ".java",
-          "AppClassTemplate.java"));
-      files.add(new FileCreator(clientDir, "GreetingService.java",
-          "RpcClientTemplate.java"));
-      files.add(new FileCreator(clientDir, "GreetingServiceAsync.java",
-          "RpcAsyncClientTemplate.java"));
-      files.add(new FileCreator(serverDir, "GreetingServiceImpl.java",
-          "RpcServerTemplate.java"));
-      files.add(new FileCreator(sharedDir, "FieldVerifier.java",
-          "SharedClassTemplate.java"));
-      if (ant) {
-        files.add(new FileCreator(outDir, "build.xml", "project.ant.xml"));
-      }
-      if (maven) {
-        files.add(new FileCreator(outDir, "pom.xml", "project.maven.xml"));
-      }
-      files.add(new FileCreator(outDir, "README.txt", "README.txt"));
-      if (junitPath != null || maven) {
-        // create the test file.
-        files.add(new FileCreator(moduleTestDir, moduleShortName
-            + "JUnit.gwt.xml", "JUnit.gwt.xml"));
-        files.add(new FileCreator(clientTestDir, moduleShortName + "Test"
-            + ".java", "JUnitClassTemplate.java"));
-      }
-    }
-    if (!noEclipse) {
-      files.add(new FileCreator(outDir, ".project", ".project"));
-      files.add(new FileCreator(outDir, ".classpath", ".classpath"));
-      files.add(new FileCreator(outDir, moduleShortName + ".launch",
-          "App.launch"));
-      if (junitPath != null || maven) {
-        files.add(new FileCreator(outDir, moduleShortName + "Test-dev.launch",
-            "JUnit-dev.launch"));
-        files.add(new FileCreator(outDir, moduleShortName + "Test-prod.launch",
-            "JUnit-prod.launch"));
-      }
-    }
+    List<FileCreator> files = getFiles(replacements);
 
     // copy source files, replacing the content as needed
     for (FileCreator fileCreator : files) {
-      URL url = WebAppCreator.class.getResource(fileCreator.sourceName + "src");
+      URL url = WebAppCreator.class.getResource(fileCreator.sourceName);
       if (url == null) {
-        throw new FileNotFoundException(fileCreator.sourceName + "src");
+        throw new WebAppCreatorException("Could not find " + fileCreator.sourceName);
       }
       File file = Utility.createNormalFile(fileCreator.destDir,
           fileCreator.destName, overwrite, ignore);
-      if (file != null) {
+      if (file == null) {
+        continue;
+      }
+      if (fileCreator.isBinary) {
+        byte[] data = Util.readURLAsBytes(url);
+        Utility.writeTemplateBinaryFile(file, data);
+      } else {
         String data = Util.readURLAsString(url);
         Utility.writeTemplateFile(file, data, replacements);
       }
@@ -506,6 +666,85 @@
     } catch (IOException e) {
       System.err.println(e.getClass().getName() + ": " + e.getMessage());
       return false;
+    } catch (WebAppCreatorException e) {
+      System.err.println(e.getClass().getName() + ": " + e.getMessage());
+      return false;
     }
   }
+
+  private Collection<? extends FileCreator> getTemplateFiles(
+      Map<String, String> replacements, File srcDirectory, File destDirectory,
+      String templateClassRoot) throws IOException {
+    List<FileCreator> files = new ArrayList<FileCreator>();
+    
+    File[] filesInDir = srcDirectory.listFiles();
+
+    for (File srcFile : filesInDir) {
+      String replacedName = replaceFileName(replacements, srcFile.getName());
+      
+      if (srcFile.isDirectory()) {
+        File newDirectory = Utility.getDirectory(destDirectory, replacedName, true);
+        files.addAll(getTemplateFiles(replacements, srcFile, newDirectory, templateClassRoot
+            + srcFile.getName() + "/"));
+      } else if (srcFile.getName().endsWith("src")) {
+        String srcFilename = templateClassRoot + srcFile.getName();
+        String destName = replacedName.substring(0, replacedName.length() - 3);
+        files.add(new FileCreator(destDirectory, destName, srcFilename, false));
+      } else if (srcFile.getName().endsWith("bin")) {
+        String srcFilename = templateClassRoot + srcFile.getName();
+        String destName = replacedName.substring(0, replacedName.length() - 3);
+        files.add(new FileCreator(destDirectory, destName, srcFilename, true));
+      } // else ... ignore everything not a directory, "*src" nor "*bin"
+    }
+    
+    return files;
+  }
+
+  private Collection<? extends FileCreator> getTemplateFilesFromZip(
+      Map<String, String> replacements, URL zipUrl, File destDirectory)
+      throws WebAppCreatorException, IOException {
+    String zipPath = zipUrl.getFile();
+    int separator = zipPath.indexOf('!');
+
+    if (separator == -1) {
+      throw new WebAppCreatorException("Error opening template zip file. '!' not found in "
+          + zipUrl);
+    }
+
+    String zipFilename = zipPath.substring(0, separator);
+    String templateDirName = zipPath.substring(separator + 2);
+    ZipFile zipFile;
+
+    try {
+      zipFile = new ZipFile(new File(new URI(zipFilename)));
+    } catch (URISyntaxException e) {
+      throw new WebAppCreatorException("Could not open Zip file. Malformed URI", e);
+    }
+
+    Enumeration<? extends ZipEntry> allZipEntries = zipFile.entries();
+
+    ArrayList<FileCreator> templateEntries = new ArrayList<FileCreator>(); 
+
+    while (allZipEntries.hasMoreElements()) {
+      ZipEntry entry = allZipEntries.nextElement();
+      String fullName = entry.getName();
+      if (fullName.startsWith(templateDirName + "/")) {
+        String relativeName = fullName.substring(templateDirName.length());
+        String replacedName = replaceFileName(replacements, relativeName);
+        if (entry.isDirectory()) {
+          Utility.getDirectory(destDirectory, replacedName, true);
+        } else if (fullName.endsWith("src")) {
+          // remove the src suffix 
+          String destName = replacedName.substring(0, replacedName.length() - 3);
+          templateEntries.add(new FileCreator(destDirectory, destName, "/" + fullName, false));
+        } else if (fullName.endsWith("bin")) {
+          String destName = replacedName.substring(0, replacedName.length() - 3);
+          templateEntries.add(new FileCreator(destDirectory, destName, "/" + fullName, true));
+        }
+      } // else ... ignore everything not a directory, "*src" nor "*bin"
+    }
+
+    return templateEntries;
+  }
+
 }
diff --git a/user/src/com/google/gwt/user/tools/WebAppCreatorException.java b/user/src/com/google/gwt/user/tools/WebAppCreatorException.java
new file mode 100644
index 0000000..935690d
--- /dev/null
+++ b/user/src/com/google/gwt/user/tools/WebAppCreatorException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.tools;
+
+/**
+ * Indicates a problem was found while creating a web application.
+ */
+public class WebAppCreatorException extends Exception {
+
+  public WebAppCreatorException(String message) {
+    super(message);
+  }
+
+  public WebAppCreatorException(String message, Exception e) {
+    super(message, e);
+  }
+
+}
diff --git a/user/src/com/google/gwt/user/tools/JUnit-dev.launchsrc b/user/src/com/google/gwt/user/tools/templates/_eclipse-test/_moduleShortName_Test-dev.launchsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/JUnit-dev.launchsrc
rename to user/src/com/google/gwt/user/tools/templates/_eclipse-test/_moduleShortName_Test-dev.launchsrc
diff --git a/user/src/com/google/gwt/user/tools/JUnit-prod.launchsrc b/user/src/com/google/gwt/user/tools/templates/_eclipse-test/_moduleShortName_Test-prod.launchsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/JUnit-prod.launchsrc
rename to user/src/com/google/gwt/user/tools/templates/_eclipse-test/_moduleShortName_Test-prod.launchsrc
diff --git a/user/src/com/google/gwt/user/tools/JUnit.gwt.xmlsrc b/user/src/com/google/gwt/user/tools/templates/_sample-test/_testFolder_/_moduleFolder_/_moduleShortName_JUnit.gwt.xmlsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/JUnit.gwt.xmlsrc
rename to user/src/com/google/gwt/user/tools/templates/_sample-test/_testFolder_/_moduleFolder_/_moduleShortName_JUnit.gwt.xmlsrc
diff --git a/user/src/com/google/gwt/user/tools/JUnitClassTemplate.javasrc b/user/src/com/google/gwt/user/tools/templates/_sample-test/_testFolder_/_moduleFolder_/client/_moduleShortName_Test.javasrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/JUnitClassTemplate.javasrc
rename to user/src/com/google/gwt/user/tools/templates/_sample-test/_testFolder_/_moduleFolder_/client/_moduleShortName_Test.javasrc
diff --git a/user/src/com/google/gwt/user/tools/project.ant.xmlsrc b/user/src/com/google/gwt/user/tools/templates/ant/build.xmlsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/project.ant.xmlsrc
rename to user/src/com/google/gwt/user/tools/templates/ant/build.xmlsrc
diff --git a/user/src/com/google/gwt/user/tools/templates/eclipse/.classpathsrc b/user/src/com/google/gwt/user/tools/templates/eclipse/.classpathsrc
new file mode 100644
index 0000000..c08900c
--- /dev/null
+++ b/user/src/com/google/gwt/user/tools/templates/eclipse/.classpathsrc
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<classpath>
+   <classpathentry kind="src" path="@srcFolder"/>@eclipseTestDir
+   <classpathentry kind="lib" path="@gwtUserPath"/>
+   <classpathentry kind="lib" path="@gwtDevPath"/>
+   <classpathentry kind="lib" path="@gwtValidationPath"/>
+   <classpathentry kind="lib" path="@gwtValidationSourcesPath"/>
+   <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+   <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+   <classpathentry kind="output" path="@warFolder/WEB-INF/classes"/>
+@serverClasspathLibs
+</classpath>
diff --git a/user/src/com/google/gwt/user/tools/templates/eclipse/.projectsrc b/user/src/com/google/gwt/user/tools/templates/eclipse/.projectsrc
new file mode 100644
index 0000000..2436f15
--- /dev/null
+++ b/user/src/com/google/gwt/user/tools/templates/eclipse/.projectsrc
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<projectDescription>
+   <name>@moduleShortName</name>
+   <comment>@moduleShortName project</comment>
+   <projects/>
+   <buildSpec>
+       <buildCommand>
+           <name>org.eclipse.jdt.core.javabuilder</name>
+           <arguments/>
+       </buildCommand>
+   </buildSpec>
+   <natures>
+       <nature>org.eclipse.jdt.core.javanature</nature>
+   </natures>
+</projectDescription>
diff --git a/user/src/com/google/gwt/user/tools/App.launchsrc b/user/src/com/google/gwt/user/tools/templates/eclipse/_moduleShortName_.launch
similarity index 100%
copy from user/src/com/google/gwt/user/tools/App.launchsrc
copy to user/src/com/google/gwt/user/tools/templates/eclipse/_moduleShortName_.launch
diff --git a/user/src/com/google/gwt/user/tools/App.launchsrc b/user/src/com/google/gwt/user/tools/templates/eclipse/_moduleShortName_.launchsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/App.launchsrc
rename to user/src/com/google/gwt/user/tools/templates/eclipse/_moduleShortName_.launchsrc
diff --git a/user/src/com/google/gwt/user/tools/project.maven.xmlsrc b/user/src/com/google/gwt/user/tools/templates/maven/pom.xmlsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/project.maven.xmlsrc
rename to user/src/com/google/gwt/user/tools/templates/maven/pom.xmlsrc
diff --git a/user/src/com/google/gwt/user/tools/README.txtsrc b/user/src/com/google/gwt/user/tools/templates/readme/README.txtsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/README.txtsrc
rename to user/src/com/google/gwt/user/tools/templates/readme/README.txtsrc
diff --git a/user/src/com/google/gwt/user/tools/Module.gwt.xmlsrc b/user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/_moduleShortName_.gwt.xmlsrc
similarity index 86%
rename from user/src/com/google/gwt/user/tools/Module.gwt.xmlsrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/_moduleShortName_.gwt.xmlsrc
index 858c3dc..2f0267e 100644
--- a/user/src/com/google/gwt/user/tools/Module.gwt.xmlsrc
+++ b/user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/_moduleShortName_.gwt.xmlsrc
@@ -6,8 +6,7 @@
   <!-- Inherit the default GWT style sheet.  You can change       -->
   <!-- the theme of your GWT application by uncommenting          -->
   <!-- any one of the following lines.                            -->
-  <inherits name='com.google.gwt.user.theme.clean.Clean'/>
-  <!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -->
+  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
   <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
   <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->
 
diff --git a/user/src/com/google/gwt/user/tools/RpcClientTemplate.javasrc b/user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/client/GreetingService.javasrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/RpcClientTemplate.javasrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/client/GreetingService.javasrc
diff --git a/user/src/com/google/gwt/user/tools/RpcAsyncClientTemplate.javasrc b/user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/client/GreetingServiceAsync.javasrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/RpcAsyncClientTemplate.javasrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/client/GreetingServiceAsync.javasrc
diff --git a/user/src/com/google/gwt/user/tools/AppClassTemplate.javasrc b/user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/client/_moduleShortName_.javasrc
similarity index 98%
rename from user/src/com/google/gwt/user/tools/AppClassTemplate.javasrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/client/_moduleShortName_.javasrc
index 8e005c7..45429c3 100644
--- a/user/src/com/google/gwt/user/tools/AppClassTemplate.javasrc
+++ b/user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/client/_moduleShortName_.javasrc
@@ -60,7 +60,6 @@
     final DialogBox dialogBox = new DialogBox();
     dialogBox.setText("Remote Procedure Call");
     dialogBox.setAnimationEnabled(true);
-    dialogBox.setGlassEnabled(true);
     final Button closeButton = new Button("Close");
     // We can set the id of a widget by accessing its Element
     closeButton.getElement().setId("closeButton");
diff --git a/user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc b/user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/server/GreetingServiceImpl.javasrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/RpcServerTemplate.javasrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/server/GreetingServiceImpl.javasrc
diff --git a/user/src/com/google/gwt/user/tools/SharedClassTemplate.javasrc b/user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/shared/FieldVerifier.javasrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/SharedClassTemplate.javasrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_srcFolder_/_moduleFolder_/shared/FieldVerifier.javasrc
diff --git a/user/src/com/google/gwt/user/tools/templates/sample/_testFolder_/_moduleFolder_/.placeholder b/user/src/com/google/gwt/user/tools/templates/sample/_testFolder_/_moduleFolder_/.placeholder
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/user/src/com/google/gwt/user/tools/templates/sample/_testFolder_/_moduleFolder_/.placeholder
diff --git a/user/src/com/google/gwt/user/tools/web.xmlsrc b/user/src/com/google/gwt/user/tools/templates/sample/_warFolder_/WEB-INF/web.xmlsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/web.xmlsrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_warFolder_/WEB-INF/web.xmlsrc
diff --git a/user/src/com/google/gwt/user/tools/AppCss.csssrc b/user/src/com/google/gwt/user/tools/templates/sample/_warFolder_/_moduleShortName_.csssrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/AppCss.csssrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_warFolder_/_moduleShortName_.csssrc
diff --git a/user/src/com/google/gwt/user/tools/AppHtml.htmlsrc b/user/src/com/google/gwt/user/tools/templates/sample/_warFolder_/_moduleShortName_.htmlsrc
similarity index 100%
rename from user/src/com/google/gwt/user/tools/AppHtml.htmlsrc
rename to user/src/com/google/gwt/user/tools/templates/sample/_warFolder_/_moduleShortName_.htmlsrc
diff --git a/user/test/com/google/gwt/user/tools/WebAppCreatorTest.java b/user/test/com/google/gwt/user/tools/WebAppCreatorTest.java
index 307361e..60fcc91 100644
--- a/user/test/com/google/gwt/user/tools/WebAppCreatorTest.java
+++ b/user/test/com/google/gwt/user/tools/WebAppCreatorTest.java
@@ -59,7 +59,7 @@
   /**
    * Default options, generate ant and eclipse files.
    */
-  public void testAppCreatorAnt() throws IOException {
+  public void testAppCreatorAnt() throws IOException, WebAppCreatorException {
     runCreator("-out", projectFolder, MY_PROJECT);
     assertFileExists(".project");
     assertFileExists(".classpath");
@@ -84,7 +84,7 @@
   /**
    * Adding a valid junit.jar, the test stuff is generated.
    */
-  public void testCreatorAntJunit() throws IOException {
+  public void testCreatorAntJunit() throws IOException, WebAppCreatorException {
     runCreator("-out", projectFolder, "-junit", mockJar, MY_PROJECT);
     assertFileExists(".project");
     assertFileExists(".classpath");
@@ -107,6 +107,14 @@
   }
 
   /**
+   * Default options, generate ant template only.
+   */
+  public void testAppCreatorAntOnly() throws IOException, WebAppCreatorException {
+    runCreator("-out", projectFolder, "-templates", "ant", MY_PROJECT);
+    assertFileExists("build.xml");
+  }
+
+  /**
    * Check illegal argument combinations.
    */
   public void testCreatorBadArguments() {
@@ -131,7 +139,7 @@
   /**
    * Do not generate eclipse files.
    */
-  public void testCreatorNoAnt() throws IOException {
+  public void testCreatorNoAnt() throws IOException, WebAppCreatorException {
     runCreator("-out", projectFolder, "-noant", "-junit", mockJar, MY_PROJECT);
     assertFileExists(".project");
     assertFileExists(".classpath");
@@ -156,7 +164,7 @@
   /**
    * Do not generate eclipse files.
    */
-  public void testCreatorNoEclipse() throws IOException {
+  public void testCreatorNoEclipse() throws IOException, WebAppCreatorException {
     runCreator("-out", projectFolder, "-XnoEclipse", "-junit", mockJar,
         MY_PROJECT);
     assertFileDoesNotExist(".project");
@@ -182,11 +190,8 @@
   /**
    * Generate a maven2 project. Note that -junit option is not needed.
    */
-  public void testCreatorMaven() throws IOException {
+  public void testCreatorMaven() throws IOException, WebAppCreatorException {
     runCreator("-out", projectFolder, "-maven", MY_PROJECT);
-    assertFileExists(".project");
-    assertFileExists(".classpath");
-    assertFileExists("build.xml");
     assertFileExists("pom.xml");
     assertFileExists("README.txt");
     assertFileExists("src/main/java/com/foo/shared/FieldVerifier.java");
@@ -202,15 +207,12 @@
     assertFileExists("src/main/webapp/WEB-INF/web.xml");
     assertFileExists("src/test/java/com/foo/client/HelloTest.java");
     assertFileExists("src/test/java/com/foo/HelloJUnit.gwt.xml");
-    assertFileExists("Hello.launch");
-    assertFileExists("HelloTest-dev.launch");
-    assertFileExists("HelloTest-prod.launch");
   }
 
   /**
    * Running generator on existing projects.
    */
-  public void testCreatorMultipleTimes() throws IOException {
+  public void testCreatorMultipleTimes() throws IOException, WebAppCreatorException {
     // Create the project
     runCreator("-out", projectFolder, MY_PROJECT);
 
@@ -239,7 +241,7 @@
   /**
    * Generate only eclipse stuff.
    */
-  public void testCreatorOnlyEclipse() throws IOException {
+  public void testCreatorOnlyEclipse() throws IOException, WebAppCreatorException {
     runCreator("-out", projectFolder, "-XonlyEclipse", "-junit", mockJar,
         MY_PROJECT);
     assertFileExists(".project");
@@ -298,8 +300,9 @@
 
   /**
    * run appWebCreator.
+   * @throws WebAppCreatorException if any template processing fails
    */
-  private void runCreator(String... args) throws IOException {
+  private void runCreator(String... args) throws IOException, WebAppCreatorException {
     WebAppCreator creator = new WebAppCreator();
     ArgProcessor argProcessor = creator.new ArgProcessor();
     if (!argProcessor.processArgs(args)) {