Package org.codehaus.enunciate.modules.gwt

Source Code of org.codehaus.enunciate.modules.gwt.GWTDeploymentModule

/*
* Copyright 2006-2008 Web Cohesion
*
* 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 org.codehaus.enunciate.modules.gwt;

import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.TypeDeclaration;
import freemarker.template.*;
import net.sf.jelly.apt.decorations.JavaDoc;
import net.sf.jelly.apt.freemarker.FreemarkerJavaDoc;
import org.apache.commons.digester.RuleSet;
import org.codehaus.enunciate.EnunciateException;
import org.codehaus.enunciate.apt.EnunciateFreemarkerModel;
import org.codehaus.enunciate.apt.EnunciateClasspathListener;
import org.codehaus.enunciate.config.SchemaInfo;
import org.codehaus.enunciate.config.WsdlInfo;
import org.codehaus.enunciate.contract.HasFacets;
import org.codehaus.enunciate.contract.jaxb.TypeDefinition;
import org.codehaus.enunciate.contract.jaxws.EndpointInterface;
import org.codehaus.enunciate.contract.jaxws.WebFault;
import org.codehaus.enunciate.contract.jaxws.WebMethod;
import org.codehaus.enunciate.contract.validation.Validator;
import org.codehaus.enunciate.contract.jaxrs.RootResource;
import org.codehaus.enunciate.contract.jaxrs.ResourceMethod;
import org.codehaus.enunciate.main.*;
import org.codehaus.enunciate.main.webapp.BaseWebAppFragment;
import org.codehaus.enunciate.main.webapp.WebAppComponent;
import org.codehaus.enunciate.modules.FacetAware;
import org.codehaus.enunciate.modules.FreemarkerDeploymentModule;
import org.codehaus.enunciate.modules.ProjectExtensionModule;
import org.codehaus.enunciate.modules.GWTHomeAwareModule;
import org.codehaus.enunciate.modules.gwt.config.GWTApp;
import org.codehaus.enunciate.modules.gwt.config.GWTAppModule;
import org.codehaus.enunciate.modules.gwt.config.GWTRuleSet;
import org.codehaus.enunciate.template.freemarker.ClientPackageForMethod;
import org.codehaus.enunciate.template.freemarker.SimpleNameWithParamsMethod;
import org.codehaus.enunciate.template.freemarker.AccessorOverridesAnotherMethod;
import org.codehaus.enunciate.util.FacetFilter;
import org.codehaus.enunciate.util.TypeDeclarationComparator;

import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* <h1>GWT Module</h1>
*
* <p>The GWT deployment module generates the server-side and client-side libraries used to support a
* <a href="http://code.google.com/webtoolkit/">GWT RPC</a> API. There is also support for invoking the
* GWTCompiler to compile a set a GWT applications that can be included in the generated Enunciate web
* application.</p>
*
* <p>The order of the GWT deployment module is 0, as it doesn't depend on any artifacts exported
* by any other module.</p>
*
* <p>This documentation is an overview of how to use Enunciate to build your GWT-RPC API and (optional)
* associated GWT application. The reader is redirected to the
* <a href="http://code.google.com/webtoolkit/">documentation for the GWT</a> for instructions on how to use GWT.
* You may also find the petclinic sample application useful as an illustration.  The sample petclinic application
* is included with the Enunciate distribution.</p>
*
* <ul>
* <li><a href="#steps">steps</a></li>
* <li><a href="#config">configuration</a></li>
* <li><a href="#artifacts">artifacts</a></li>
* </ul>
*
* <h1><a name="steps">Steps</a></h1>
*
* <h3>generate</h3>
*
* <p>The "generate" step generates all source code for the GWT-RPC API.</p>
*
* <h3>compile</h3>
*
* <p>During the "compile" step, the GWT module compiles the code that was generated. It is also during the "compile" step that
* the GWTCompiler is invoked on any GWT applications that were specified in the configuration.</p>
*
* <h3>build</h3>
*
* <p>The "build" step assembles the client-side GWT jar.</p>
*
* <h1><a name="config">Configuration</a></h1>
*
* <p>The GWT module is configured by the "gwt" element under the "modules" element of the
* enunciate configuration file.  <b>The GWT module is disabled by default because of the
* added constraints applied to the service endpoints.</b>  To enable GWT, be sure to specify
* <i>disabled="false"</i> on the "gwt" element.</p>
*
* <p>The "gwt" element supports the following attributes:</p>
*
* <ul>
* <li>The "rpcModuleName" attribute <b>must</b> be supplied.  The RPC module name will also be used to
* determine the layout of the created module.  The module name must be of the form "com.mycompany.MyModuleName".
* In this example, "com.mycompany" will be the <i>module namespace</i> and all client code will be generated into
* a package named of the form [module namespace].client (e.g. "com.mycompany.client").  By default, in order to provide
* a sensible mapping from service code to GWT client-side code, all service endpoints, faults, and JAXB beans must
* exist in a package that matches the module namespace, or a subpackage thereof.  Use the "enforceNamespaceConformance"
* attribute to loosen this requirement.</li>
* <li>The "enforceNamespaceConformance" attribute allows you to lift the requirement that all classes must exist in a package
* that matches the module namespace.  If this is set to "false", the classes that do not match the module namespace will
* be subpackaged by the client namespace.  <i>NOTE: You may not like this because the package mapping might be ugly.</i>  For example,
* if your module namespace is "com.mycompany" and you have a class "org.othercompany.OtherClass", it will be mapped to a client-side GWT class
* named "com.mycompany.client.org.othercompany.OtherClass".</li>
* <li>The "label" attribute is used to determine the name of the client-side artifact files. The default is the Enunciate project label.</li>
* <li>The "clientJarName" attribute specifies the name of the client-side jar file that is to be created.
* If no jar name is specified, the name will be calculated from the enunciate label, or a default will
* be supplied.</li>
* <li>The "clientJarDownloadable" attribute specifies whether the GWT client-side jar should be included as a
* download.  Default: <code>false</code>.</li>
* <li>The "gwtHome" attribute specifies the filesystem path to the Google Web Toolkit home directory.</li>
* <li>The "gwtCompilerClass" attribute specifies the FQN of the GWTCompiler.  Default: "com.google.gwt.dev.GWTCompiler".</li>
* <li>The "enforceNoFieldAccessors" attribute specifies whether to enforce that a field accessor cannot be used for GWT mapping.
* <i>Note: whether this option is enabled or disabled, there currently MUST be a getter and setter for each accessor.  This option only
* disables the compile-time validation check.</i></li>
* <li>The "useWrappedServices" attribute specifies whether to use wrapped GWT client services. This is an artifact from when GWT 1.4 was supported
* and the generic types were unavailable. Default: false</li>
* <li>The "disableCompile" attribute prevents Enunciate from compiling its generated client source files.</li>
* </ul>
*
* <h3>The "war" element</h3>
*
* <p>The "war" element under the "gwt" element is used to configure the webapp that will host the GWT endpoints and GWT applications. It supports
* the following attributes:</p>
*
* <ul>
* <li>The "gwtSubcontext" attribute is the subcontext at which the gwt endpoints will be mounted.  Default: "/gwt/".</li>
* <li>The "gwtAppDir" attribute is the directory in the war to which the gwt applications will be put.  The default is the root of the war.</li>
* </ul>
*
* <h3>The "facets" element</h3>
*
* <p>The "facets" element is applicable to the GWT module to configure which facets are to be included/excluded from the GWT artifacts. For
* more information, see <a href="http://docs.codehaus.org/display/ENUNCIATE/Enunciate+API+Facets">API Facets</a></p>
*
* <h3>The "app" element</h3>
*
* <p>The GWT module supports the development of GWT AJAX apps.  Each app is comprised of a set of GWT modules that will be compiled into JavaScript.
* The "app" element supports the folowing attributes:</p>
*
* <ul>
* <li>The "name" attribute is the name of the GWT app.  Each app will be deployed into a subdirectory that matches its name.  By default,
* the name of the application is the empty string ("").  This means that the application will be deployed into the root directory.</li>
* <li>The "srcDir" attribute specifies the source directory for the application. This attribute is required.</li>
* <li>The "javascriptStyle" attribute specified the JavaScript style that is to be applied by the GWTCompiler.  Valid values are "OBF", "PRETTY",
* and "DETAILED". The default value is "OBF".</li>
* </ul>
*
* <p>Each "app" element may contain an arbitrary number of "module" child elements that specify the modules that are included in the app.
* The "module" element supports the following attributes:</p>
*
* <ul>
* <li>The "name" attribute specifies the name of the module. This is usually of the form "com.mycompany.MyModule" and it always has a corresponding
* ".gwt.xml" module file.</li>
* <li>The "outputDir" attribute specifies where the compiled module will be placed, relative to the application directory.  By default, the
* outputDir is the empty string (""), which means the compiled module will be placed at the root of the application directory. <u>Note: as of GWT 1.6,
* a new "war" directory structure is supported, along with support to control the directory where the GWT compiler puts the compiled application. Because
* of this, the "outputDir" attribute will only be honored if not using GWT 1.6 or above.</u></li>
* <li>The "shellPage" attribute specifies the (usually HTML) page to open when invoking the shell for this module (used to generate the shell script). By
* default, the shell page is the [moduleId].html, where [moduleId] is the (short, unqualified) name of the module.</li>
* </ul>
*
* <h3>The "gwtCompileJVMArg" element</h3>
*
* <p>The "gwtCompileJVMArg" element is used to specify additional JVM parameters that will be used when invoking GWTCompile.  It supports a single
* "value" attribute.</p>
*
* <h3>The "gwtCompilerArg" element</h3>
*
* <p>The "gwtCompilerArg" element is used to specify additional arguments that will be psssed to the GWT compiler.  It supports a single
* "value" attribute.</p>
*
* <h3>Example Configuration</h3>
*
* <p>As an example, consider the following configuration:</p>
*
* <code class="console">
* &lt;enunciate&gt;
* &nbsp;&nbsp;&lt;modules&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;gwt disabled="false"
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rpcModuleName="com.mycompany.MyGWTRPCModule"
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gwtHome="/home/myusername/tools/gwt-linux-1.5.2"&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;facets&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/facets&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;app srcDir="src/main/mainapp"&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;module name="com.mycompany.apps.main.MyRootModule"/&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;module name="com.mycompany.apps.main.MyModuleTwo" outputDir="two"/&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/app&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;app srcDir="src/main/anotherapp" name="another"&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;module name="com.mycompany.apps.another.AnotherRootModule"/&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;module name="com.mycompany.apps.another.MyModuleThree" outputDir="three"/&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/app&gt;
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...
* &nbsp;&nbsp;&nbsp;&nbsp;&lt;/gwt&gt;
* &nbsp;&nbsp;&lt;/modules&gt;
* &lt;/enunciate&gt;
* </code>
*
* <p>The configuration enables the GWT Enunciate module and will publish the web service endpoints under the module name
* "com.mycompany.MyGWTRPCModule".</p>
*
* <p>There are also two GWT applications defined. The first is located at "src/main/mainapp". Since there is
* no "name" applied to this application, it will be generated into the root of the applications directory.  This
* first application has two GWT modules defined, the first named "com.mycompany.apps.main.MyRootModule" and the second
* named "com.mycompany.apps.main.MyModuleTwo".  "MyRootModule", since it has to output path defined, will be generated
* into the root of its application directory (which is the root of the main applications directory).  "MyModuleTwo", however,
* will be generated into the subdirectory "two".</p>
*
* <p>The second application, rooted at "src/main/anotherapp", is named "another", so it will be generated into the "another"
* subdirectory of the main applications directory.  It also has two modules, one named "com.mycompany.apps.another.AnotherRootModule",
* and another named "com.mycompany.apps.another.MyModuleThree".  "AnotherRootModule" will be generated into the root of its application
* directory ("another") and "MyModuleThree" will be generated into "another/three".</p>
*
* <p>All modules are defined by their associated ".gwt.xml" module definition files.  After the "compile" step of the GWT module, the
* main applications directory will look like this:</p>
*
* <code class="console">
* |--[output of com.mycompany.apps.main.MyRootModule]
* |--two
* |----[output of com.mycompany.apps.main.MyModuleTwo]
* |--another
* |----[output of com.mycompany.apps.another.AnotherRootModule]
* |----three
* |------[output of com.mycompany.apps.another.MyModuleThree]
* </code>
*
* <p>For a less contrived example, see the "petclinic" sample Enunciate project bundled with the Enunciate distribution.</p>
*
* <h1><a name="artifacts">Artifacts</a></h1>
*
* <ul>
* <li>The "gwt.client.jar" artifact is the packaged client-side GWT jar.</li>
* <li>The "gwt.client.src.dir" artifact is the directory where the client-side source code is generated.</li>
* <li>The "gwt.server.src.dir" artifact is the directory where the server-side source code is generated.</li>
* <li>The "gwt.app.dir" artifact is the directory to which the GWT AJAX apps are compiled.</li>
* <li>The "[appName].[moduleName].shell" artifact is the GWT shell script used to invoke the gwt shell for the module [moduleName] in app [appName].</li>
* Script is different from the alternative in that it assumes a server is already running before invoking the GWTShell.</li>
* </ul>
*
* @author Ryan Heaton
* @docFileName module_gwt.html
*/
public class GWTDeploymentModule extends FreemarkerDeploymentModule implements ProjectExtensionModule, GWTHomeAwareModule, EnunciateClasspathListener, FacetAware {

  private boolean forceGenerateJsonOverlays = false;
  private boolean disableJsonOverlays = false;
  private boolean enforceNamespaceConformance = true;
  private boolean enforceNoFieldAccessors = true;
  private String rpcModuleNamespace = null;
  private String rpcModuleName = null;
  private String clientJarName = null;
  private boolean clientJarDownloadable = false;
  private final List<GWTApp> gwtApps = new ArrayList<GWTApp>();
  private final GWTRuleSet configurationRules = new GWTRuleSet();
  private String gwtHome = System.getProperty("gwt.home") == null ? System.getenv("GWT_HOME") : System.getProperty("gwt.home");
  private final List<String> gwtCompileJVMArgs = new ArrayList<String>();
  private final List<String> gwtCompilerArgs = new ArrayList<String>();
  private String gwtCompilerClass;
  private String gwtSubcontext = "/gwt";
  private String gwtAppDir = null;
  private boolean useWrappedServices = false;
  private boolean gwtRtFound = false;
  private boolean springDIFound = false;
  private boolean jacksonXcAvailable = false;
  private String label = null;
  private int[] gwtVersion = null;
  private GWTModuleClasspathHandler gwtClasspathHandler;
  private boolean disableCompile = false;
  private Set<String> facetIncludes = new TreeSet<String>();
  private Set<String> facetExcludes = new TreeSet<String>(Arrays.asList("org.codehaus.enunciate.modules.gwt.GWTTransient"));

  /**
   * @return "gwt"
   */
  @Override
  public String getName() {
    return "gwt";
  }

  @Override
  public void init(Enunciate enunciate) throws EnunciateException {
    super.init(enunciate);

    if (!isDisabled()) {
      if (rpcModuleName == null) {
        throw new EnunciateException("You must specify a \"rpcModuleName\" for the GWT module.");
      }
      if (rpcModuleNamespace == null) {
        throw new EnunciateException("You must specify a \"rpcModuleNamespace\" for the GWT module.");
      }

      gwtClasspathHandler = new GWTModuleClasspathHandler(enunciate);
      enunciate.addClasspathHandler(gwtClasspathHandler);

      if (gwtApps.size() > 0) {
        if (this.gwtHome == null) {
          throw new EnunciateException("To compile a GWT app you must specify the GWT home directory, either in configuration, by setting the GWT_HOME environment variable, or setting the 'gwt.home' system property.");
        }

        for (GWTApp gwtApp : gwtApps) {
          String srcPath = gwtApp.getSrcDir();

          if (srcPath == null) {
            throw new EnunciateException("A source directory for the GWT app "
              + ("".equals(gwtApp.getName()) ? "" : "'" + gwtApp.getName() + "' ")
              + "must be supplied with the 'srcDir' attribute.");
          }

          File srcDir = enunciate.resolvePath(srcPath);
          if (!srcDir.exists()) {
            throw new EnunciateException("Source directory '" + srcDir.getAbsolutePath() + "' doesn't exist for the GWT app"
              + ("".equals(gwtApp.getName()) ? "." : " '" + gwtApp.getName() + "'."));
          }
        }
      }

      if (this.gwtVersion == null) {
        int[] gwtVersion = null; //default to 1.5
        if (this.gwtHome != null) {
          File about = new File(gwtHome, "about.txt");
          if (about.exists()) {
            try {
              BufferedReader reader = new BufferedReader(new FileReader(about));
              String line = reader.readLine();
              if (line != null) {
                Matcher matcher = Pattern.compile("[\\d\\.]+").matcher(line);
                if (matcher.find()) {
                  String gwtVersionStr = matcher.group();
                  if (getEnunciate().isDebug()) {
                    getEnunciate().debug("Targeting GWT version %s according to %s.", gwtVersionStr, (this.gwtHome + File.separatorChar + "about.txt"));
                  }

                  try {
                    gwtVersion = parseGwtVersion(gwtVersionStr);
                  }
                  catch (NumberFormatException e) {
                    getEnunciate().warn("Invalid GWT version %s according to %s.", gwtVersionStr, (this.gwtHome + File.separatorChar + "about.txt"));
                  }
                }
                else {
                  getEnunciate().warn("Unable to determine GWT version from %s.", (this.gwtHome + File.separatorChar + "about.txt"));
                }
              }
              reader.close();
            }
            catch (IOException e) {
              //fall through...
            }
          }
        }

        if (gwtVersion == null) {
          gwtVersion = new int[]{1, 5};
        }

        this.gwtVersion = gwtVersion;
      }

      if (this.gwtVersion.length < 2) {
        throw new IllegalStateException("Illegal GWT version.");
      }

      if (!gwtVersionGreaterThan(1, 4)) {
        throw new EnunciateException(String.format("As of version 1.15, Enunciate no longer supports versions of GWT less than 1.5. " +
          "It appears GWT %s.%s is being used, according to %s. If this is an invalid assessment, you can get around this error by " +
          "setting the correct version with the 'gwtVersion' attribute of the Enunciate GWT module configuration.",
                                                   this.gwtVersion[0],
                                                   this.gwtVersion[1],
                                                   (this.gwtHome + File.separatorChar + "about.txt")));
      }

      if (getGwtCompilerClass() == null) {
        setGwtCompilerClass(gwtVersionGreaterThan(1, 5) ? "com.google.gwt.dev.Compiler" : "com.google.gwt.dev.GWTCompiler");
      }
    }
  }

  protected boolean gwtVersionGreaterThan(int major, int minor) {
    return (this.gwtVersion[0] == major) ? (this.gwtVersion[1] > minor) : (this.gwtVersion[0] > major);
  }

  @Override
  public void initModel(EnunciateFreemarkerModel model) {
    super.initModel(model);

    if (!isDisabled()) {
      if (!gwtRtFound && !isGenerateJsonOverlays()) {
        info("GWT runtime wasn't found on the classpath. If you're doing GWT-RPC, you're going to fail at runtime.");
      }
    }
  }

  public void onClassesFound(Set<String> classes) {
    gwtRtFound |= classes.contains("org.codehaus.enunciate.modules.gwt.GWTEndpointImpl");
    springDIFound |= classes.contains("org.springframework.beans.factory.annotation.Autowired");
    jacksonXcAvailable |= classes.contains("org.codehaus.jackson.xc.JaxbAnnotationIntrospector");
  }

  @Override
  public void doFreemarkerGenerate() throws IOException, TemplateException, EnunciateException {
    File clientSideGenerateDir = getClientSideGenerateDir();
    File serverSideGenerateDir = getServerSideGenerateDir();
    boolean upToDate = enunciate.isUpToDateWithSources(clientSideGenerateDir) && enunciate.isUpToDateWithSources(serverSideGenerateDir);
    if (!upToDate) {
      //load the references to the templates....
      URL typeMapperTemplate = getTemplateURL("gwt-type-mapper.fmt");
      URL enumTypeMapperTemplate = getTemplateURL("gwt-enum-15-type-mapper.fmt");
      URL faultMapperTemplate = getTemplateURL("gwt-fault-mapper.fmt");
      URL moduleXmlTemplate = getTemplateURL("gwt-module-xml.fmt");

      URL eiTemplate = isUseWrappedServices() ? getTemplateURL("gwt-legacy-endpoint-interface.fmt") : getTemplateURL("gwt-endpoint-interface.fmt");
      URL endpointImplTemplate = getTemplateURL("gwt-endpoint-impl.fmt");
      URL faultTemplate = getTemplateURL("gwt-fault.fmt");
      URL typeTemplate = getTemplateURL("gwt-type.fmt");
      URL overlayTypeTemplate = getTemplateURL("gwt-overlay-type.fmt");
      URL enumTypeTemplate = getTemplateURL("gwt-enum-15-type.fmt");
      URL overlayEnumTypeTemplate = getTemplateURL("gwt-enum-overlay-type.fmt");

      EnunciateFreemarkerModel model = getModel();
      model.put("useSpringDI", this.springDIFound);
      model.put("useWrappedServices", this.isUseWrappedServices());
      Map<String, String> conversions = new LinkedHashMap<String, String>();
      Set<String> knownGwtPackages = this.gwtClasspathHandler != null ? this.gwtClasspathHandler.getSourcePackagesToModules().keySet() : Collections.<String>emptySet();
      for (String knownGwtPackage : knownGwtPackages) {
        //makes sure any known gwt packages are preserved.
        conversions.put(knownGwtPackage, knownGwtPackage);
      }
      Map<String, String> overlayConversions = new HashMap<String, String>();
      String clientNamespace = this.rpcModuleNamespace + ".client";
      conversions.put(this.rpcModuleNamespace, clientNamespace);
      overlayConversions.put(this.rpcModuleNamespace, clientNamespace + ".json");
      if (!this.enforceNamespaceConformance) {
        TreeSet<WebFault> allFaults = new TreeSet<WebFault>(new TypeDeclarationComparator());
        for (WsdlInfo wsdlInfo : model.getNamespacesToWSDLs().values()) {
          for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) {
            if (!isFacetExcluded(ei)) {
              String pckg = ei.getPackage().getQualifiedName();
              if (!pckg.startsWith(this.rpcModuleNamespace) && !conversions.containsKey(pckg)) {
                conversions.put(pckg, clientNamespace + "." + pckg);
              }
              for (WebMethod webMethod : ei.getWebMethods()) {
                for (WebFault webFault : webMethod.getWebFaults()) {
                  allFaults.add(webFault);
                }
              }
            }
          }
        }
        for (WebFault webFault : allFaults) {
          if (!isFacetExcluded(webFault)) {
            String pckg = webFault.getPackage().getQualifiedName();
            if (!pckg.startsWith(this.rpcModuleNamespace) && !conversions.containsKey(pckg) && (getKnownGwtModule(webFault) == null)) {
              conversions.put(pckg, clientNamespace + "." + pckg);
            }
          }
        }
        for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
          for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
            if (!isFacetExcluded(typeDefinition)) {
              String pckg = typeDefinition.getPackage().getQualifiedName();
              if (!pckg.startsWith(this.rpcModuleNamespace) && !conversions.containsKey(pckg)) {
                if (getKnownGwtModule(typeDefinition) == null) {
                  conversions.put(pckg, clientNamespace + "." + pckg);
                }
                overlayConversions.put(pckg, clientNamespace + ".json." + pckg);
              }
            }
          }
        }
      }

      ClientClassnameForMethod classnameFor = new ClientClassnameForMethod(conversions);
      classnameFor.setJdk15(true);
      OverlayClientClassnameForMethod overlayClassnameFor = new OverlayClientClassnameForMethod(overlayConversions);
      overlayClassnameFor.setJdk15(true);
      model.put("packageFor", new ClientPackageForMethod(conversions));
      model.put("overlayPackageFor", new ClientPackageForMethod(overlayConversions));
      model.put("isAccessorOfTypeLong", new IsAccessorOfTypeLongMethod());
      model.put("classnameFor", classnameFor);
      model.put("overlayClassnameFor", overlayClassnameFor);
      model.put("simpleNameFor", new SimpleNameWithParamsMethod(classnameFor));
      model.put("overlaySimpleNameFor", new SimpleNameWithParamsMethod(overlayClassnameFor));
      model.put("gwtSubcontext", getGwtSubcontext());
      model.put("accessorOverridesAnother", new AccessorOverridesAnotherMethod());

      model.setFileOutputDirectory(clientSideGenerateDir);
      Properties gwt2jaxbMappings = new Properties();
      TreeSet<WebFault> allFaults = new TreeSet<WebFault>(new TypeDeclarationComparator());

      Set<String> importedModules = new TreeSet<String>();
      if (isGenerateRPCSupport()) {
        debug("Generating the GWT endpoints...");
        for (WsdlInfo wsdlInfo : model.getNamespacesToWSDLs().values()) {
          for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) {
            if (!isFacetExcluded(ei)) {
              model.put("endpointInterface", ei);
              processTemplate(eiTemplate, model);

              for (WebMethod webMethod : ei.getWebMethods()) {
                for (WebFault webFault : webMethod.getWebFaults()) {
                  allFaults.add(webFault);
                }
              }
            }
          }
        }

        debug("Generating the GWT faults...");
        for (WebFault webFault : allFaults) {
          if (!isFacetExcluded(webFault)) {
            String knownGwtModule = getKnownGwtModule(webFault);
            if (knownGwtModule == null) {
              model.put("fault", webFault);
              processTemplate(faultTemplate, model);
            }
            else {
              importedModules.add(knownGwtModule);
              debug("Skipping generating fault for %s because it's in a known GWT module.", webFault.getQualifiedName());
            }
          }
        }

        debug("Generating the GWT types...");
        for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
          for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
            if (!isFacetExcluded(typeDefinition)) {
              String knownGwtModule = getKnownGwtModule(typeDefinition);
              if (knownGwtModule == null) {

                model.put("type", typeDefinition);

                URL template = typeDefinition.isEnum() ? enumTypeTemplate : typeTemplate;
                processTemplate(template, model);
              }
              else {
                importedModules.add(knownGwtModule);
                debug("Skipping generating GWT type for %s because it's in a known GWT module.", typeDefinition.getQualifiedName());
              }
            }
          }
        }
      }

      if (isGenerateJsonOverlays()) {
        debug("Generating the GWT json overlay types...");
        for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
          for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
            if (!isFacetExcluded(typeDefinition)) {
              model.put("type", typeDefinition);
              URL template = typeDefinition.isEnum() ? overlayEnumTypeTemplate : overlayTypeTemplate;
              processTemplate(template, model);
            }
          }
        }
      }

      model.put("gwtModuleName", this.rpcModuleName);
      model.put("importedGwtModules", importedModules);
      processTemplate(moduleXmlTemplate, model);

      model.setFileOutputDirectory(serverSideGenerateDir);
      if (isGenerateRPCSupport()) {
        debug("Generating the GWT endpoint implementations...");
        for (WsdlInfo wsdlInfo : model.getNamespacesToWSDLs().values()) {
          for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) {
            if (!isFacetExcluded(ei)) {
              model.put("endpointInterface", ei);
              processTemplate(endpointImplTemplate, model);
            }
          }
        }

        debug("Generating the GWT type mappers...");
        for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
          for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
            if (!isFacetExcluded(typeDefinition)) {
              if (typeDefinition.isEnum()) {
                model.put("type", typeDefinition);
                processTemplate(enumTypeMapperTemplate, model);
                gwt2jaxbMappings.setProperty(classnameFor.convert(typeDefinition), typeDefinition.getQualifiedName());
              }
              else if (getKnownGwtModule(typeDefinition) == null) {
                if (!typeDefinition.isEnum()) {
                  model.put("type", typeDefinition);
                  processTemplate(typeMapperTemplate, model);
                  gwt2jaxbMappings.setProperty(classnameFor.convert(typeDefinition), typeDefinition.getQualifiedName());
                }
              }
              else {
                debug("Skipping generation of type mapper for %s because it's a known GWT type.", typeDefinition.getQualifiedName());
              }
            }
          }
        }

        debug("Generating the GWT fault mappers...");
        for (WebFault webFault : allFaults) {
          if (!isFacetExcluded(webFault) && (getKnownGwtModule(webFault) == null)) {
            model.put("fault", webFault);
            processTemplate(faultMapperTemplate, model);
            gwt2jaxbMappings.setProperty(classnameFor.convert(webFault), webFault.getQualifiedName());
          }
        }

        FileOutputStream mappingsOut = new FileOutputStream(new File(serverSideGenerateDir, "gwt-to-jaxb-mappings.properties"));
        gwt2jaxbMappings.store(mappingsOut, "mappings for gwt classes to jaxb classes.");
        mappingsOut.flush();
        mappingsOut.close();
      }
    }
    else {
      info("Skipping GWT source generation as everything appears up-to-date...");
    }

    enunciate.addArtifact(new FileArtifact(getName(), "gwt.client.src.dir", clientSideGenerateDir));
    enunciate.addArtifact(new FileArtifact(getName(), "gwt.server.src.dir", serverSideGenerateDir));

    enunciate.addAdditionalSourceRoot(clientSideGenerateDir); //server-side also uses client-side classes.
    enunciate.addAdditionalSourceRoot(serverSideGenerateDir);
  }

  private String getKnownGwtModule(TypeDeclaration declaration) {
    Set<String> knownGwtPackages = this.gwtClasspathHandler != null ? this.gwtClasspathHandler.getSourcePackagesToModules().keySet() : Collections.<String>emptySet();
    String declPackage = declaration.getPackage() == null ? "" : declaration.getPackage().getQualifiedName();
    for (String knownGwtPackage : knownGwtPackages) {
      if (declPackage.startsWith(knownGwtPackage)) {
        return this.gwtClasspathHandler.getSourcePackagesToModules().get(knownGwtPackage);
      }
    }

    return null;
  }

  /**
   * Invokes GWTCompile on the apps specified in the configuration file.
   */
  protected void doGWTCompile() throws EnunciateException, IOException {
    if (this.gwtHome == null) {
      throw new EnunciateException("To compile a GWT app you must specify the GWT home directory, either in configuration, by setting the GWT_HOME environment variable, or setting the 'gwt.home' system property.");
    }

    File gwtHomeDir = new File(this.gwtHome);
    if (!gwtHomeDir.exists()) {
      throw new EnunciateException("GWT home not found ('" + gwtHomeDir.getAbsolutePath() + "').");
    }

    File gwtUserJar = new File(gwtHomeDir, "gwt-user.jar");
    if (!gwtUserJar.exists()) {
      warn("Unable to find %s. You may be GWT compile errors.", gwtUserJar.getAbsolutePath());
    }

    //now we have to find gwt-dev.jar.
    //start by assuming linux...
    File gwtDevJar = new File(gwtHomeDir, "gwt-dev.jar");
    if (!gwtDevJar.exists()) {
      File linuxDevJar = new File(gwtHomeDir, "gwt-dev-linux.jar");
      gwtDevJar = linuxDevJar;

      if (!gwtDevJar.exists()) {
        //linux not found. try mac...
        File macDevJar = new File(gwtHomeDir, "gwt-dev-mac.jar");
        gwtDevJar = macDevJar;

        if (!gwtDevJar.exists()) {
          //okay, we'll try windows if we have to...
          File windowsDevJar = new File(gwtHomeDir, "gwt-dev-windows.jar");
          gwtDevJar = windowsDevJar;

          if (!gwtDevJar.exists()) {
            throw new EnunciateException(String.format("Unable to find GWT dev jar. Looked for %s, %s, and %s.", linuxDevJar.getAbsolutePath(), macDevJar.getAbsolutePath(), windowsDevJar.getAbsolutePath()));
          }
        }
      }
    }

    boolean windows = false;
    File javaBinDir = new File(System.getProperty("java.home"), "bin");
    File javaExecutable = new File(javaBinDir, "java");
    if (!javaExecutable.exists()) {
      //append the "exe" for windows users.
      javaExecutable = new File(javaBinDir, "java.exe");
      windows = true;
    }

    String javaCommand = javaExecutable.getAbsolutePath();
    if (!javaExecutable.exists()) {
      warn("No java executable found in %s.  We'll just hope the environment is set up to execute 'java'...", javaBinDir.getAbsolutePath());
      javaCommand = "java";
    }

    StringBuilder classpath = new StringBuilder(enunciate.getEnunciateRuntimeClasspath());
    //append the client-side gwt directory.
    classpath.append(File.pathSeparatorChar).append(getClientSideGenerateDir().getAbsolutePath());
    //append the gwt-user jar.
    classpath.append(File.pathSeparatorChar).append(gwtUserJar.getAbsolutePath());
    //append the gwt-dev jar.
    classpath.append(File.pathSeparatorChar).append(gwtDevJar.getAbsolutePath());

    //so here's the GWT compile command:
    //java [extra jvm args] -cp [classpath] [compilerClass] -gen [gwt-gen-dir] -style [style] -out [out] [moduleName]
    List<String> jvmargs = getGwtCompileJVMArgs();
    List<String> compilerArgs = getGwtCompilerArgs();
    List<String> gwtcCommand = new ArrayList<String>(jvmargs.size() + compilerArgs.size() + 11);
    int argIndex = 0;
    gwtcCommand.add(argIndex++, javaCommand);
    for (String arg : jvmargs) {
      gwtcCommand.add(argIndex++, arg);
    }
    gwtcCommand.add(argIndex++, "-cp");
    int classpathArgIndex = argIndex; //app-specific arg.
    gwtcCommand.add(argIndex++, null);
    int compileClassIndex = argIndex;
    gwtcCommand.add(argIndex++, getGwtCompilerClass());
    gwtcCommand.add(argIndex++, "-gen");
    gwtcCommand.add(argIndex++, getGwtGenDir().getAbsolutePath());
    gwtcCommand.add(argIndex++, "-style");
    int styleArgIndex = argIndex;
    gwtcCommand.add(argIndex++, null); //app-specific arg.
    gwtcCommand.add(argIndex++, gwtVersionGreaterThan(1, 5) ? "-war" : "-out");
    int outArgIndex = argIndex;
    gwtcCommand.add(argIndex++, null); //app-specific arg.
    for (String arg : compilerArgs) {
      gwtcCommand.add(argIndex++, arg);
    }
    int moduleNameIndex = argIndex;
    gwtcCommand.add(argIndex, null); //module-specific arg.

    for (GWTApp gwtApp : gwtApps) {
      String appName = gwtApp.getName();
      File appSource = enunciate.resolvePath(gwtApp.getSrcDir());
      String style = gwtApp.getJavascriptStyle().toString();
      File appDir = getAppGenerateDir(appName);

      gwtcCommand.set(classpathArgIndex, classpath.toString() + File.pathSeparatorChar + appSource.getAbsolutePath());
      gwtcCommand.set(styleArgIndex, style);
      gwtcCommand.set(outArgIndex, appDir.getAbsolutePath());

      boolean upToDate = enunciate.isUpToDate(getClientSideGenerateDir(), appDir) && enunciate.isUpToDate(appSource, appDir);
      if (!upToDate) {
        for (GWTAppModule appModule : gwtApp.getModules()) {
          String moduleName = appModule.getName();

          gwtcCommand.set(moduleNameIndex, moduleName);
          debug("Executing GWTCompile for module '%s'...", moduleName);
          if (enunciate.isDebug()) {
            StringBuilder command = new StringBuilder();
            for (String commandPiece : gwtcCommand) {
              command.append(' ').append(commandPiece);
            }
            debug("Executing GWTCompile for module %s with the command: %s", moduleName, command);
          }
          ProcessBuilder processBuilder = new ProcessBuilder(gwtcCommand);
          processBuilder.directory(getGenerateDir());
          processBuilder.redirectErrorStream(true);
          Process process = processBuilder.start();
          BufferedReader procReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
          String line = procReader.readLine();
          while (line != null) {
            line = URLDecoder.decode(line, "utf-8").replaceAll("%", "%%").trim(); //GWT URL-encodes spaces and other weird Windows characters.
            info(line);
            line = procReader.readLine();
          }
          int procCode;
          try {
            procCode = process.waitFor();
          }
          catch (InterruptedException e1) {
            throw new EnunciateException("Unexpected inturruption of the GWT compile process.");
          }

          if (procCode != 0) {
            throw new EnunciateException("GWT compile failed for module " + moduleName);
          }

          if (!gwtVersionGreaterThan(1, 5)) {
            File moduleOutputDir = appDir;
            String outputPath = appModule.getOutputPath();
            if ((outputPath != null) && (!"".equals(outputPath.trim()))) {
              moduleOutputDir = new File(appDir, outputPath);
            }

            File moduleGenDir = new File(appDir, moduleName);
            if (!moduleOutputDir.equals(moduleGenDir)) {
              moduleOutputDir.mkdirs();
              enunciate.copyDir(moduleGenDir, moduleOutputDir);
              deleteDir(moduleGenDir);
            }
          }

          StringBuilder shellCommand = new StringBuilder();
          for (int i = 0; i < moduleNameIndex; i++) {
            String commandArg = gwtcCommand.get(i);
            if (i == compileClassIndex) {
              commandArg = gwtVersionGreaterThan(1, 5) ? "com.google.gwt.dev.HostedMode" : "com.google.gwt.dev.GWTShell";
            }
            else if (commandArg.indexOf(' ') >= 0) {
              commandArg = '"' + commandArg + '"';
            }

            shellCommand.append(commandArg).append(' ');
          }

          //add any extra args before the module name.
          shellCommand.append(windows ? "%*" : "$@").append(' ');

          String shellPage = getModuleId(moduleName) + ".html";
          if (appModule.getShellPage() != null) {
            shellPage = appModule.getShellPage();
          }

          if (!gwtVersionGreaterThan(1, 5)) {
            //when invoking the shell for GWT 1.4 or 1.5, it requires a URL to load.
            //The URL is the [moduleName]/[shellPage.html]
            shellCommand.append(moduleName).append('/').append(shellPage);
          }
          else {
            //as of 1.6, you invoke it with -startupUrl [shellPage.html] [moduleName]
            shellCommand.append("-startupUrl ").append(shellPage).append(' ').append(moduleName);
          }

          File scriptFile = getShellScriptFile(appName, moduleName);
          scriptFile.getParentFile().mkdirs();
          FileWriter writer = new FileWriter(scriptFile);
          writer.write(shellCommand.toString());
          writer.flush();
          writer.close();

          File shellFile = getShellScriptFile(appName, moduleName);
          if (shellFile.exists()) {
            StringBuilder scriptArtifactId = new StringBuilder();
            if ((appName != null) && (appName.trim().length() > 0)) {
              scriptArtifactId.append(appName).append('.');
            }
            scriptArtifactId.append(moduleName).append(".shell");
            getEnunciate().addArtifact(new FileArtifact(getName(), scriptArtifactId.toString(), shellFile));
          }
          else {
            debug("No GWT shell script file exists at %s.  No artifact added.", shellFile);
          }
        }
      }
      else {
        info("Skipping GWT compile for app %s as everything appears up-to-date...", appName);
      }
    }
  }

  /**
   * Delete a directory on the filesystem.
   *
   * @param dir The directory to delete.
   * @return Whether the directory was successfully deleted.
   */
  private boolean deleteDir(File dir) {
    if (dir.exists()) {
      File[] files = dir.listFiles();
      for (File file : files) {
        if (file.isDirectory()) {
          deleteDir(file);
        }
        else {
          file.delete();
        }
      }
    }
    return dir.delete();
  }

  @Override
  protected void doCompile() throws EnunciateException, IOException {
    Enunciate enunciate = getEnunciate();

    if (this.gwtApps.size() > 0) {
      doGWTCompile();
      enunciate.addArtifact(new FileArtifact(getName(), "gwt.app.dir", getAppGenerateDir()));
    }

    if (!enunciate.isUpToDate(getClientSideGenerateDir(), getClientSideCompileDir())) {
      debug("Compiling the GWT client-side files...");
      Collection<String> clientSideFiles = enunciate.getJavaFiles(getClientSideGenerateDir());
      String clientClasspath = enunciate.getRuntimeClasspath();
      if (!isDisableCompile()) {
        enunciate.invokeJavac(clientClasspath, "1.5", "1.5", getClientSideCompileDir(), new ArrayList<String>(), clientSideFiles.toArray(new String[clientSideFiles.size()]));
      }
      else {
        info("Compilation of GWT Java sources has been disabled.");
      }
    }
    else {
      info("Skipping compile of GWT client-side files because everything appears up-to-date...");
    }
  }

  @Override
  protected void doBuild() throws EnunciateException, IOException {
    buildClientJar();

    //assemble the server-side webapp fragment
    BaseWebAppFragment webAppFragment = new BaseWebAppFragment(getName());

    //base webapp dir...
    File webappDir = new File(getBuildDir(), "webapp");
    webappDir.mkdirs();

    File gwtCompileDir = getAppGenerateDir();
    if ((this.gwtApps.size() > 0) && (gwtCompileDir != null) && (gwtCompileDir.exists())) {
      File gwtAppDir = webappDir;
      if ((getGwtAppDir() != null) && (!"".equals(getGwtAppDir()))) {
        debug("Gwt applications will be put into the %s subdirectory of the web application.", getGwtAppDir());
        gwtAppDir = new File(webappDir, getGwtAppDir());
      }
      getEnunciate().copyDir(gwtCompileDir, gwtAppDir, new File(gwtCompileDir, ".gwt-tmp"));
    }
    else {
      debug("No gwt apps were found.");
    }
    webAppFragment.setBaseDir(webappDir);

    File classesDir = new File(new File(webappDir, "WEB-INF"), "classes");
    File gwtToJaxbMappings = new File(getServerSideGenerateDir(), "gwt-to-jaxb-mappings.properties");
    if (gwtToJaxbMappings.exists()) {
      if (!enunciate.isUpToDate(gwtToJaxbMappings, new File(classesDir, "gwt-to-jaxb-mappings.properties"))) {
        enunciate.copyFile(gwtToJaxbMappings, new File(classesDir, "gwt-to-jaxb-mappings.properties"));
      }
      else {
        info("Skipping gwt-to-jaxb mappings copy because everything appears up to date!");
      }
    }

    //servlets.
    ArrayList<WebAppComponent> servlets = new ArrayList<WebAppComponent>();
    for (WsdlInfo wsdlInfo : getModel().getNamespacesToWSDLs().values()) {
      for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) {
        WebAppComponent gwtServlet = new WebAppComponent();
        gwtServlet.setClassname(ei.getPackage().getQualifiedName() + ".gwt.GWT" + ei.getSimpleName() + "Impl");
        gwtServlet.setName("GWT" + ei.getSimpleName());
        TreeSet<String> urlMappings = new TreeSet<String>();
        urlMappings.add(getGwtSubcontext() + '/' + ei.getServiceName());
        gwtServlet.setUrlMappings(urlMappings);
        servlets.add(gwtServlet);
      }
    }
    webAppFragment.setServlets(servlets);

    getEnunciate().addWebAppFragment(webAppFragment);
  }

  protected void buildClientJar() throws IOException, EnunciateException {
    Enunciate enunciate = getEnunciate();
    String clientJarName = getClientJarName();

    if (clientJarName == null) {
      String label = "enunciate";
      if (getLabel() != null) {
        label = getLabel();
      }
      else if ((enunciate.getConfig() != null) && (enunciate.getConfig().getLabel() != null)) {
        label = enunciate.getConfig().getLabel();
      }

      clientJarName = label + "-gwt-client.jar";
    }

    File clientJar = new File(getBuildDir(), clientJarName);
    if (!enunciate.isUpToDate(getClientSideGenerateDir(), clientJar)) {
      enunciate.copyDir(getClientSideGenerateDir(), getClientSideCompileDir());
      enunciate.zip(clientJar, getClientSideCompileDir());
    }
    else {
      info("GWT client jar appears up-to-date...");
    }

    List<ArtifactDependency> clientDeps = new ArrayList<ArtifactDependency>();
    MavenDependency gwtUserDependency = new MavenDependency();
    gwtUserDependency.setId("gwt-user");
    gwtUserDependency.setArtifactType("jar");
    gwtUserDependency.setDescription("Base GWT classes.");
    gwtUserDependency.setGroupId("com.google.gwt");
    gwtUserDependency.setURL("http://code.google.com/webtoolkit/");
    gwtUserDependency.setVersion(String.format("%s.%s", this.gwtVersion[0], this.gwtVersion[1]));
    clientDeps.add(gwtUserDependency);

    ClientLibraryArtifact gwtClientArtifact = new ClientLibraryArtifact(getName(), "gwt.client.library", "GWT Client Library");
    gwtClientArtifact.setPlatform("JavaScript/GWT");
    //read in the description from file:
    gwtClientArtifact.setDescription(readResource("library_description.fmt"));
    NamedFileArtifact clientArtifact = new NamedFileArtifact(getName(), "gwt.client.jar", clientJar);
    clientArtifact.setDescription("The binaries and sources for the GWT client library.");
    clientArtifact.setPublic(clientJarDownloadable);
    clientArtifact.setArtifactType(ArtifactType.binaries);
    gwtClientArtifact.addArtifact(clientArtifact);
    gwtClientArtifact.setDependencies(clientDeps);
    enunciate.addArtifact(clientArtifact);
  }

  /**
   * Reads a resource into string form.
   *
   * @param resource The resource to read.
   * @return The string form of the resource.
   */
  protected String readResource(String resource) throws IOException, EnunciateException {
    HashMap<String, Object> model = new HashMap<String, Object>();
    model.put("sample_service_method", getModelInternal().findExampleWebMethod());
    model.put("sample_resource", getModelInternal().findExampleResourceMethod());

    URL res = GWTDeploymentModule.class.getResource(resource);
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    PrintStream out = new PrintStream(bytes);
    try {
      processTemplate(res, model, out);
      out.flush();
      bytes.flush();
      return bytes.toString("utf-8");
    }
    catch (TemplateException e) {
      throw new EnunciateException(e);
    }
  }

  /**
   * Whether the given type declaration is GWT-transient.
   *
   * @param declaration The type declaration.
   * @return Whether the given tyep declaration is GWT-transient.
   */
  protected boolean isFacetExcluded(HasFacets declaration) {
    return !FacetFilter.accept(declaration);
  }

  /**
   * Whether the given type declaration is GWT-transient.
   *
   * @param declaration The type declaration.
   * @return Whether the given tyep declaration is GWT-transient.
   */
  protected boolean isGWTTransient(Declaration declaration) {
    return declaration != null && declaration.getAnnotation(GWTTransient.class) != null;
  }

  /**
   * Get a template URL for the template of the given name.
   *
   * @param template The specified template.
   * @return The URL to the specified template.
   */
  protected URL getTemplateURL(String template) {
    return GWTDeploymentModule.class.getResource(template);
  }

  /**
   * Get the generate directory for server-side GWT classes.
   *
   * @return The generate directory for server-side GWT classes.
   */
  public File getServerSideGenerateDir() {
    return new File(getGenerateDir(), "server");
  }

  /**
   * Get the generate directory for client-side GWT classes.
   *
   * @return The generate directory for client-side GWT classes.
   */
  public File getClientSideGenerateDir() {
    return new File(getGenerateDir(), "client");
  }

  /**
   * The GWT gen directory.  (I still don't know what this is used for, exactly.)
   *
   * @return The GWT gen directory.
   */
  public File getGwtGenDir() {
    return new File(getGenerateDir(), ".gwt-gen");
  }

  /**
   * Get the compile directory for client-side GWT classes.
   *
   * @return The compile directory for client-side GWT classes.
   */
  public File getClientSideCompileDir() {
    return new File(getCompileDir(), "client");
  }

  /**
   * The base generate dir for the gwt applications.
   *
   * @return The base generate dir for the gwt applications.
   */
  public File getAppGenerateDir() {
    return new File(getCompileDir(), "gwtapps");
  }

  /**
   * The base generate dir for the gwt scripts (e.g. shell scripts).
   *
   * @return The base generate dir for the gwt scripts (e.g. shell scripts).
   */
  public File getGwtScriptDir() {
    return new File(getCompileDir(), "scripts");
  }

  /**
   * Get the GWT shell script file for the specified module, app.
   *
   * @param appName    The app name.
   * @param moduleName The module name.
   * @return The shell script file.
   */
  public File getShellScriptFile(String appName, String moduleName) {
    StringBuilder filename = new StringBuilder();
    if ((appName != null) && (appName.trim().length() > 0)) {
      filename.append(appName).append('-');
    }

    String moduleId = getModuleId(moduleName);
    filename.append(moduleId).append(".gwt-shell");
    File scriptDir = getGwtScriptDir();
    if (!scriptDir.exists()) {
      scriptDir.mkdirs();
    }
    return new File(scriptDir, filename.toString());
  }

  /**
   * Get the module id of the specified GWT module.
   *
   * @param moduleName The module name.
   * @return The module id.
   */
  protected String getModuleId(String moduleName) {
    String moduleId = moduleName;
    int lastDot = moduleName.lastIndexOf('.');
    if (lastDot >= 0) {
      moduleId = moduleName.substring(lastDot + 1);
    }
    return moduleId;
  }

  /**
   * The generate dir for the specified app.
   *
   * @param appName The app name.
   * @return The generate dir for the specified app.
   */
  protected File getAppGenerateDir(String appName) {
    File appsDir = getAppGenerateDir();
    if ("".equals(appName)) {
      return appsDir;
    }
    else {
      return new File(appsDir, appName);
    }
  }

  /**
   * The name of the client jar.
   *
   * @return The name of the client jar.
   */
  public String getClientJarName() {
    return clientJarName;
  }

  /**
   * The name of the client jar.
   *
   * @param clientJarName The name of the client jar.
   */
  public void setClientJarName(String clientJarName) {
    this.clientJarName = clientJarName;
  }

  /**
   * GWT configuration rule set.
   *
   * @return GWT configuration rule set.
   */
  @Override
  public RuleSet getConfigurationRules() {
    return this.configurationRules;
  }

  /**
   * GWT validator.
   *
   * @return GWT validator.
   */
  @Override
  public Validator getValidator() {
    return new GWTValidator(this.rpcModuleNamespace,
                            this.gwtClasspathHandler != null ? this.gwtClasspathHandler.getSourcePackagesToModules().keySet() : Collections.<String>emptySet(),
                            this.enforceNamespaceConformance,
                            this.enforceNoFieldAccessors, forceGenerateJsonOverlays);
  }

  @Override
  protected ObjectWrapper getObjectWrapper() {
    return new DefaultObjectWrapper() {
      @Override
      public TemplateModel wrap(Object obj) throws TemplateModelException {
        if (obj instanceof JavaDoc) {
          return new FreemarkerJavaDoc((JavaDoc) obj);
        }

        return super.wrap(obj);
      }
    };
  }

  /**
   * The generated RPC module name.
   *
   * @return The generated RPC module name.
   */
  public String getRpcModuleName() {
    return rpcModuleName;
  }

  /**
   * The generated RPC module namespace.
   *
   * @return The generated RPC module namespace.
   */
  public String getRpcModuleNamespace() {
    return rpcModuleNamespace;
  }

  /**
   * The generated RPC module name.
   *
   * @param gwtModuleName The gwt module name.
   * @deprecated Use {@link #setRpcModuleName(String)}
   */
  public void setGwtModuleName(String gwtModuleName) {
    setRpcModuleName(gwtModuleName);
  }

  /**
   * The generated RPC module name.
   *
   * @param rpcModuleName The generated RPC module name.
   */
  public void setRpcModuleName(String rpcModuleName) {
    this.rpcModuleName = rpcModuleName;
    int lastDot = rpcModuleName.lastIndexOf('.');
    if (lastDot < 0) {
      throw new IllegalArgumentException("The gwt module name must be of the form 'gwt.module.ns.ModuleName'");
    }
    this.rpcModuleNamespace = rpcModuleName.substring(0, lastDot);
  }

  /**
   * Whether the client jar is downloadable.
   *
   * @return Whether the client jar is downloadable.
   */
  public boolean isClientJarDownloadable() {
    return clientJarDownloadable;
  }

  /**
   * Whether the client jar is downloadable.
   *
   * @param clientJarDownloadable Whether the client jar is downloadable.
   */
  public void setClientJarDownloadable(boolean clientJarDownloadable) {
    this.clientJarDownloadable = clientJarDownloadable;
  }

  /**
   * Whether to enforce namespace conformace on the server-side classes.
   *
   * @return Whether to enforce namespace conformace on the server-side classes.
   */
  public boolean isEnforceNamespaceConformance() {
    return enforceNamespaceConformance;
  }

  /**
   * Whether to enforce namespace conformace on the server-side classes.
   *
   * @param enforceNamespaceConformance Whether to enforce namespace conformace on the server-side classes.
   */
  public void setEnforceNamespaceConformance(boolean enforceNamespaceConformance) {
    this.enforceNamespaceConformance = enforceNamespaceConformance;
  }

  /**
   * Whether to enforce that field accessors can't be used.
   *
   * @return Whether to enforce that field accessors can't be used.
   */
  public boolean isEnforceNoFieldAccessors() {
    return enforceNoFieldAccessors;
  }

  /**
   * Whether to enforce that field accessors can't be used.
   *
   * @param enforceNoFieldAccessors Whether to enforce that field accessors can't be used.
   */
  public void setEnforceNoFieldAccessors(boolean enforceNoFieldAccessors) {
    this.enforceNoFieldAccessors = enforceNoFieldAccessors;
  }

  /**
   * The gwt home directory
   *
   * @return The gwt home directory
   */
  public String getGwtHome() {
    return gwtHome;
  }

  /**
   * Set the path to the GWT home directory.
   *
   * @param gwtHome The gwt home directory
   */
  public void setGwtHome(String gwtHome) {
    this.gwtHome = gwtHome;
  }

  /**
   * Extra JVM args for the GWT compile.
   *
   * @return Extra JVM args for the GWT compile.
   */
  public List<String> getGwtCompileJVMArgs() {
    return gwtCompileJVMArgs;
  }

  /**
   * Extra JVM args for the GWT compile.
   *
   * @param arg Extra JVM args for the GWT compile.
   */
  public void addGwtCompileJVMArg(String arg) {
    this.gwtCompileJVMArgs.add(arg);
  }

  /**
   * Additional arguments to pass to the GWT compiler.
   *
   * @return Additional arguments to pass to the GWT compiler.
   */
  public List<String> getGwtCompilerArgs() {
    return gwtCompilerArgs;
  }

  /**
   * Additional argument to pass to the GWT compiler.
   *
   * @param arg The additional arg.
   */
  public void addGwtCompilerArg(String arg) {
    this.gwtCompilerArgs.add(arg);
  }

  /**
   * The GWT compiler class.
   *
   * @return The GWT compiler class.
   */
  public String getGwtCompilerClass() {
    return gwtCompilerClass;
  }

  /**
   * The GWT compiler class.
   *
   * @param gwtCompilerClass The GWT compiler class.
   */
  public void setGwtCompilerClass(String gwtCompilerClass) {
    this.gwtCompilerClass = gwtCompilerClass;
  }

  /**
   * The gwt apps to compile.
   *
   * @return The gwt apps to compile.
   */
  public List<GWTApp> getGwtApps() {
    return gwtApps;
  }

  /**
   * Adds a gwt app to be compiled.
   *
   * @param gwtApp The gwt app to be compiled.
   */
  public void addGWTApp(GWTApp gwtApp) {
    this.gwtApps.add(gwtApp);
  }

  /**
   * The GWT subcontext.
   *
   * @return The GWT subcontext.
   */
  public String getGwtSubcontext() {
    return gwtSubcontext;
  }

  /**
   * The gwt subcontext.
   *
   * @param gwtSubcontext The gwt subcontext.
   */
  public void setGwtSubcontext(String gwtSubcontext) {
    if (gwtSubcontext == null) {
      throw new IllegalArgumentException("The GWT context must not be null.");
    }

    if ("".equals(gwtSubcontext)) {
      throw new IllegalArgumentException("The GWT context must not be the emtpy string.");
    }

    if (!gwtSubcontext.startsWith("/")) {
      gwtSubcontext = "/" + gwtSubcontext;
    }

    while (gwtSubcontext.endsWith("/")) {
      gwtSubcontext = gwtSubcontext.substring(gwtSubcontext.length() - 1);
    }

    this.gwtSubcontext = gwtSubcontext;
  }

  /**
   * The gwt app dir.
   *
   * @return The gwt app dir.
   */
  public String getGwtAppDir() {
    return gwtAppDir;
  }

  /**
   * The gwt app dir.
   *
   * @param gwtAppDir The gwt app dir.
   */
  public void setGwtAppDir(String gwtAppDir) {
    this.gwtAppDir = gwtAppDir;
  }

  /**
   * Whether to generated wrapped GWT remote services in the client-code.
   *
   * @return Whether to generated wrapped GWT remote services in the client-code.
   */
  public boolean isUseWrappedServices() {
    return useWrappedServices;
  }

  /**
   * Whether to generated wrapped GWT remote services in the client-code.
   *
   * @param useWrappedServices Whether to generated wrapped GWT remote services in the client-code.
   */
  public void setUseWrappedServices(boolean useWrappedServices) {
    this.useWrappedServices = useWrappedServices;
  }

  /**
   * Whether to generate JSON overlays.
   *
   * @return Whether to generate JSON overlays.
   */
  public boolean isGenerateJsonOverlays() {
    return forceGenerateJsonOverlays || (!disableJsonOverlays && jacksonXcAvailable && existsAnyJsonResourceMethod(getModelInternal().getRootResources()));
  }

  /**
   * Whether to generate the RPC support classes.
   *
   * @return Whether to generate the RPC support classes.
   */
  public boolean isGenerateRPCSupport() {
    return !getModelInternal().getNamespacesToWSDLs().isEmpty();
  }

  /**
   * Whether to generate JSON overlays.
   *
   * @param generateJsonOverlays Whether to generate JSON overlays.
   */
  public void setGenerateJsonOverlays(boolean generateJsonOverlays) {
    this.forceGenerateJsonOverlays = generateJsonOverlays;
    this.disableJsonOverlays = !generateJsonOverlays;
  }

  /**
   * The label for the GWT client API.
   *
   * @return The label for the GWT client API.
   */
  public String getLabel() {
    return label;
  }

  /**
   * The label for the ActionScript API.
   *
   * @param label The label for the ActionScript API.
   */
  public void setLabel(String label) {
    this.label = label;
  }

  /**
   * Sets the GWT version that Enunciate will target.
   *
   * @param version The GWT version Enunciate will target.
   */
  public void setGwtVersion(String version) {
    this.gwtVersion = parseGwtVersion(version);
  }

  protected int[] parseGwtVersion(String version) throws NumberFormatException {
    String[] versionStr = version.split("\\.");
    int[] gwtVersion = new int[Math.max(versionStr.length, 2)];
    for (int i = 0; i < versionStr.length; i++) {
      String versionToken = versionStr[i];
      gwtVersion[i] = Integer.parseInt(versionToken);
    }
    return gwtVersion;
  }

  /**
   * Whether any root resources exist that produce json.
   *
   * @param rootResources The root resources.
   * @return Whether any root resources exist that produce json.
   */
  protected boolean existsAnyJsonResourceMethod(List<RootResource> rootResources) {
    for (RootResource rootResource : rootResources) {
      for (ResourceMethod resourceMethod : rootResource.getResourceMethods(true)) {
        for (String mime : resourceMethod.getProducesMime()) {
          if ("*/*".equals(mime)) {
            return true;
          }
          else if (mime.toLowerCase().contains("json")) {
            return true;
          }
        }
        for (String mime : resourceMethod.getConsumesMime()) {
          if ("*/*".equals(mime)) {
            return true;
          }
          else if (mime.toLowerCase().contains("json")) {
            return true;
          }
        }
      }
    }
    return false;
  }

  public List<File> getProjectSources() {
    List<File> projectSources = new ArrayList<File>();
    projectSources.add(getClientSideGenerateDir());
    projectSources.add(getServerSideGenerateDir());
    for (GWTApp gwtApp : getGwtApps()) {
      File srcDir = getEnunciate().resolvePath(gwtApp.getSrcDir());
      projectSources.add(srcDir);
    }
    return projectSources;
  }

  public List<File> getProjectTestSources() {
    return Collections.emptyList();
  }

  public List<File> getProjectResourceDirectories() {
    return Collections.emptyList();
  }

  public List<File> getProjectTestResourceDirectories() {
    return Collections.emptyList();
  }

  /**
   * The set of facets to include.
   *
   * @return The set of facets to include.
   */
  public Set<String> getFacetIncludes() {
    return facetIncludes;
  }

  /**
   * Add a facet include.
   *
   * @param name The name.
   */
  public void addFacetInclude(String name) {
    if (name != null) {
      this.facetIncludes.add(name);
    }
  }

  /**
   * The set of facets to exclude.
   *
   * @return The set of facets to exclude.
   */
  public Set<String> getFacetExcludes() {
    return facetExcludes;
  }

  /**
   * Add a facet exclude.
   *
   * @param name The name.
   */
  public void addFacetExclude(String name) {
    if (name != null) {
      this.facetExcludes.add(name);
    }
  }

  /**
   * Whether to disable the compilation of the java sources (default: false).
   *
   * @return Whether to disable the compilation of the java sources (default: false).
   */
  public boolean isDisableCompile() {
    return disableCompile;
  }

  /**
   * Whether to disable the compilation of the java sources (default: false).
   *
   * @param disableCompile Whether to disable the compilation of the java sources (default: false).
   */
  public void setDisableCompile(boolean disableCompile) {
    this.disableCompile = disableCompile;
  }
}
TOP

Related Classes of org.codehaus.enunciate.modules.gwt.GWTDeploymentModule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.