Initial support for WAR deployment. GWTCompiler happens to be backwards-compatible with the legacy format, but the behavior of the shell differs. Therefore we created a new entry point, GWTHosted, which is the new GWTShell and runs with WAR assumptions.
Summary of WAR mode:
- Assumes output folder is in WAR format
- Runs Jetty; embeds GWTShellServletFilter to autogenerate modules on demand
- Uses true linking (and the new relinking); dumps resources and generated selection script directly into output folder
- Ignores the <servlet> tag; servlets must be initialized via WEB-INF/web.xml in the output folder
Summary of legacy mode:
- Same behavior as GWT 1.5
- Uses Tomcat (but we want to eventually replace with Jetty); uses GWTShellServlet
- Serves files directly from public path and work directory; uses the old HostedModeLinker to generate the selection script
- <servlet> tag still works
Additionally, a GWT module file now supports a "deploy-to" attribute at the top level, which allows the subdirectory within the output folder for a module to be overridden (the default is the fully-qualified module name).
Patch by: scottb, bruce, bobv (two-pair programming)
Review by: bobv, scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@3890 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/build.xml b/dev/core/build.xml
index bcc6f13..e5090e6 100755
--- a/dev/core/build.xml
+++ b/dev/core/build.xml
@@ -26,6 +26,7 @@
<zipfileset src="${gwt.tools.lib}/apache/tapestry-util-text-4.0.2.jar" />
<zipfileset src="${gwt.tools.lib}/apache/ant-1.6.5.jar" />
<zipfileset src="${gwt.tools.lib}/eclipse/jdt-3.3.1.jar" />
+ <zipfileset src="${gwt.tools.lib}/jetty/jetty-6.1.11.jar" />
<zipfileset src="${gwt.tools.lib}/tomcat/ant-launcher-1.6.5.jar" />
<zipfileset src="${gwt.tools.lib}/tomcat/catalina-1.0.jar" />
<zipfileset src="${gwt.tools.lib}/tomcat/catalina-optional-1.0.jar" />
diff --git a/dev/core/src/com/google/gwt/core/ext/Linker.java b/dev/core/src/com/google/gwt/core/ext/Linker.java
index 5cce5e6..5dc6ff1 100644
--- a/dev/core/src/com/google/gwt/core/ext/Linker.java
+++ b/dev/core/src/com/google/gwt/core/ext/Linker.java
@@ -23,6 +23,19 @@
* the relative ordering of the Linkers. Exact order of Linker execution will be
* determined by the order of <code>add-linker</code> tags in the module
* configuration.
+ *
+ * <p>
+ * A new instance of a linker is created each time a module is compiled or
+ * during hosted mode when a module first loads (or is refreshed). During a
+ * compile, {@link #link(TreeLogger, LinkerContext, ArtifactSet)} will be called
+ * exactly once, and the artifact set will contain any and all generated
+ * artifacts. . In hosted mode,
+ * {@link #link(TreeLogger, LinkerContext, ArtifactSet)} is called initially,
+ * but with no generated artifacts. If any artifacts are subsequently generated
+ * during the course of running hosted mode,
+ * {@link #relink(TreeLogger, LinkerContext, ArtifactSet)} will be called with
+ * the new artifacts.
+ * </p>
*/
public abstract class Linker {
/**
@@ -42,4 +55,26 @@
*/
public abstract ArtifactSet link(TreeLogger logger, LinkerContext context,
ArtifactSet artifacts) throws UnableToCompleteException;
+
+ /**
+ * Re-invoke the Linker with newly generated artifacts. Linkers that need to
+ * reference the original artifact set passed into
+ * {@link #link(TreeLogger, LinkerContext, ArtifactSet)} should retain a copy
+ * of the original artifact set in an instance variable.
+ *
+ * @param logger the TreeLogger to record to
+ * @param context provides access to the Linker's environment
+ * @param newArtifacts an unmodifiable view of the newly generated artifacts
+ * @return the new artifacts that should be propagated through the linker
+ * chain; it is not necessary to return any artifacts from the
+ * original link (or previous calls to relink) that have not been
+ * modified
+ * @throws UnableToCompleteException if compilation violates assumptions made
+ * by the Linker or for errors encountered by the Linker
+ */
+ @SuppressWarnings("unused")
+ public ArtifactSet relink(TreeLogger logger, LinkerContext context,
+ ArtifactSet newArtifacts) throws UnableToCompleteException {
+ return newArtifacts;
+ }
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java b/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java
index 96435bc..6e865c0 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java
@@ -177,4 +177,9 @@
public <T> T[] toArray(T[] a) {
return treeSet.toArray(a);
}
+
+ @Override
+ public String toString() {
+ return treeSet.toString();
+ }
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/EmittedArtifact.java b/dev/core/src/com/google/gwt/core/ext/linker/EmittedArtifact.java
index 85c4ed2..96bc46b 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/EmittedArtifact.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/EmittedArtifact.java
@@ -27,6 +27,9 @@
* be emitted by the compiler into the module's output directory. This type may
* be extended by Linker providers to provide alternative implementations of
* {@link #getContents(TreeLogger)}.
+ *
+ * TODO(bobv): provide a timestamp so we can make the time on output files match
+ * that of input files?
*/
public abstract class EmittedArtifact extends Artifact<EmittedArtifact> {
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
index fd058b6..290de43 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
@@ -58,12 +58,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
-import java.util.Stack;
import java.util.TreeSet;
/**
@@ -107,7 +105,6 @@
private final SortedSet<ConfigurationProperty> configurationProperties;
private final JJSOptions jjsOptions;
- private final List<Class<? extends Linker>> linkerClasses;
private final Map<Class<? extends Linker>, String> linkerShortNames = new HashMap<Class<? extends Linker>, String>();
private final String moduleFunctionName;
@@ -117,17 +114,59 @@
private final Map<String, StandardCompilationResult> resultsByStrongName = new HashMap<String, StandardCompilationResult>();
private final SortedSet<SelectionProperty> selectionProperties;
+ private final Linker[] linkers;
+
public StandardLinkerContext(TreeLogger logger, ModuleDef module,
- JJSOptions jjsOptions) {
+ JJSOptions jjsOptions) throws UnableToCompleteException {
logger = logger.branch(TreeLogger.DEBUG,
"Constructing StandardLinkerContext", null);
this.jjsOptions = jjsOptions;
this.moduleFunctionName = module.getFunctionName();
this.moduleName = module.getName();
- this.linkerClasses = new ArrayList<Class<? extends Linker>>(
- module.getActiveLinkers());
- linkerClasses.add(module.getActivePrimaryLinker());
+
+ // Sort the linkers into the order they should actually run.
+ List<Class<? extends Linker>> sortedLinkers = new ArrayList<Class<? extends Linker>>();
+
+ // Get all the pre-linkers first.
+ for (Class<? extends Linker> linkerClass : module.getActiveLinkers()) {
+ Order order = linkerClass.getAnnotation(LinkerOrder.class).value();
+ assert (order != null);
+ if (order == Order.PRE) {
+ sortedLinkers.add(linkerClass);
+ }
+ }
+
+ // Get the primary linker.
+ sortedLinkers.add(module.getActivePrimaryLinker());
+
+ // Get all the post-linkers IN REVERSE ORDER.
+ {
+ List<Class<? extends Linker>> postLinkerClasses = new ArrayList<Class<? extends Linker>>();
+ for (Class<? extends Linker> linkerClass : module.getActiveLinkers()) {
+ Order order = linkerClass.getAnnotation(LinkerOrder.class).value();
+ assert (order != null);
+ if (order == Order.POST) {
+ postLinkerClasses.add(linkerClass);
+ }
+ }
+ Collections.reverse(postLinkerClasses);
+ sortedLinkers.addAll(postLinkerClasses);
+ }
+
+ linkers = new Linker[sortedLinkers.size()];
+ int i = 0;
+ for (Class<? extends Linker> linkerClass : sortedLinkers) {
+ try {
+ linkers[i++] = linkerClass.newInstance();
+ } catch (InstantiationException e) {
+ logger.log(TreeLogger.ERROR, "Unable to create Linker", e);
+ throw new UnableToCompleteException();
+ } catch (IllegalAccessException e) {
+ logger.log(TreeLogger.ERROR, "Unable to create Linker", e);
+ throw new UnableToCompleteException();
+ }
+ }
for (Map.Entry<String, Class<? extends Linker>> entry : module.getLinkers().entrySet()) {
linkerShortNames.put(entry.getValue(), entry.getKey());
@@ -262,77 +301,39 @@
/**
* Run the linker stack.
*/
- public ArtifactSet invokeLinkerStack(TreeLogger logger)
+ public ArtifactSet invokeLink(TreeLogger logger)
throws UnableToCompleteException {
ArtifactSet workingArtifacts = new ArtifactSet(artifacts);
- Stack<Linker> linkerStack = new Stack<Linker>();
- EnumSet<Order> phasePre = EnumSet.of(Order.PRE, Order.PRIMARY);
- EnumSet<Order> phasePost = EnumSet.of(Order.POST);
-
- // Instantiate instances of the Linkers
- for (Class<? extends Linker> clazz : linkerClasses) {
- Linker linker;
-
- // Create an instance of the Linker
- try {
- linker = clazz.newInstance();
- linkerStack.push(linker);
- } catch (InstantiationException e) {
- logger.log(TreeLogger.ERROR, "Unable to create LinkerContextShim", e);
- throw new UnableToCompleteException();
- } catch (IllegalAccessException e) {
- logger.log(TreeLogger.ERROR, "Unable to create LinkerContextShim", e);
- throw new UnableToCompleteException();
- }
-
- // Detemine if we need to invoke the Linker in the current link phase
- Order order = clazz.getAnnotation(LinkerOrder.class).value();
- if (!phasePre.contains(order)) {
- continue;
- }
-
- // The primary Linker is guaranteed to be last in the order
- if (order == Order.PRIMARY) {
- assert linkerClasses.get(linkerClasses.size() - 1).equals(clazz);
- }
-
+ for (Linker linker : linkers) {
TreeLogger linkerLogger = logger.branch(TreeLogger.TRACE,
"Invoking Linker " + linker.getDescription(), null);
-
workingArtifacts.freeze();
try {
workingArtifacts = linker.link(linkerLogger, this, workingArtifacts);
- } catch (Exception e) {
+ } catch (Throwable e) {
linkerLogger.log(TreeLogger.ERROR, "Failed to link", e);
throw new UnableToCompleteException();
}
}
+ return workingArtifacts;
+ }
- // Pop the primary linker off of the stack
- linkerStack.pop();
+ public ArtifactSet invokeRelink(TreeLogger logger,
+ ArtifactSet newlyGeneratedArtifacts) throws UnableToCompleteException {
+ ArtifactSet workingArtifacts = new ArtifactSet(newlyGeneratedArtifacts);
- // Unwind the stack
- while (!linkerStack.isEmpty()) {
- Linker linker = linkerStack.pop();
- Class<? extends Linker> linkerType = linker.getClass();
-
- // See if the Linker should be run in the current phase
- Order order = linkerType.getAnnotation(LinkerOrder.class).value();
- if (phasePost.contains(order)) {
- TreeLogger linkerLogger = logger.branch(TreeLogger.TRACE,
- "Invoking Linker " + linker.getDescription(), null);
-
- workingArtifacts.freeze();
- try {
- workingArtifacts = linker.link(linkerLogger, this, workingArtifacts);
- } catch (Exception e) {
- linkerLogger.log(TreeLogger.ERROR, "Failed to link", e);
- throw new UnableToCompleteException();
- }
+ for (Linker linker : linkers) {
+ TreeLogger linkerLogger = logger.branch(TreeLogger.TRACE,
+ "Invoking relink on Linker " + linker.getDescription(), null);
+ workingArtifacts.freeze();
+ try {
+ workingArtifacts = linker.relink(linkerLogger, this, workingArtifacts);
+ } catch (Throwable e) {
+ linkerLogger.log(TreeLogger.ERROR, "Failed to relink", e);
+ throw new UnableToCompleteException();
}
}
-
return workingArtifacts;
}
@@ -402,11 +403,20 @@
return out.toString();
}
+ /**
+ * Writes artifacts into output directories in the standard way.
+ *
+ * @param logger logs the operation
+ * @param artifacts the set of artifacts to write
+ * @param outputPath the output path for deployable artifacts
+ * @param extraPath optional extra path for non-deployable artifacts
+ * @throws UnableToCompleteException
+ */
public void produceOutputDirectory(TreeLogger logger, ArtifactSet artifacts,
- File moduleOutDir, File moduleAuxDir) throws UnableToCompleteException {
+ File outputPath, File extraPath) throws UnableToCompleteException {
- logger = logger.branch(TreeLogger.INFO, "Linking compilation into "
- + moduleOutDir.getPath(), null);
+ logger = logger.branch(TreeLogger.TRACE, "Linking compilation into "
+ + outputPath.getPath(), null);
for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) {
TreeLogger artifactLogger = logger.branch(TreeLogger.DEBUG,
@@ -414,13 +424,18 @@
File outFile;
if (artifact.isPrivate()) {
- outFile = new File(getLinkerAuxDir(moduleAuxDir, artifact.getLinker()),
- artifact.getPartialPath());
+ if (extraPath == null) {
+ continue;
+ }
+ outFile = new File(getExtraPathForLinker(extraPath,
+ artifact.getLinker()), artifact.getPartialPath());
} else {
- outFile = new File(moduleOutDir, artifact.getPartialPath());
+ outFile = new File(outputPath, artifact.getPartialPath());
}
- assert !outFile.exists() : "Attempted to overwrite " + outFile.getPath();
+ // TODO(scottb): figure out how to do a clean.
+ // assert !outFile.exists() : "Attempted to overwrite " +
+ // outFile.getPath();
Util.copy(logger, artifact.getContents(artifactLogger), outFile);
}
}
@@ -429,15 +444,11 @@
* Creates a linker-specific subdirectory in the module's auxiliary output
* directory.
*/
- private File getLinkerAuxDir(File moduleAuxDir,
+ private File getExtraPathForLinker(File extraPath,
Class<? extends Linker> linkerType) {
- // The auxiliary directory is create lazily
- if (!moduleAuxDir.exists()) {
- moduleAuxDir.mkdirs();
- }
assert linkerShortNames.containsKey(linkerType) : linkerType.getName()
+ " unknown";
- File toReturn = new File(moduleAuxDir, linkerShortNames.get(linkerType));
+ File toReturn = new File(extraPath, linkerShortNames.get(linkerType));
if (!toReturn.exists()) {
toReturn.mkdirs();
}
diff --git a/dev/core/src/com/google/gwt/dev/CompileArgProcessor.java b/dev/core/src/com/google/gwt/dev/CompileArgProcessor.java
new file mode 100644
index 0000000..d19ae45
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/CompileArgProcessor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008 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.dev;
+
+import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
+import com.google.gwt.dev.util.arg.ArgHandlerModuleName;
+import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
+import com.google.gwt.dev.util.arg.ArgHandlerTreeLoggerFlag;
+import com.google.gwt.util.tools.ToolBase;
+
+abstract class CompileArgProcessor extends ToolBase {
+ public CompileArgProcessor(CompileTaskOptions options) {
+ registerHandler(new ArgHandlerLogLevel(options));
+ registerHandler(new ArgHandlerTreeLoggerFlag(options));
+ registerHandler(new ArgHandlerOutDir(options));
+ registerHandler(new ArgHandlerModuleName(options));
+ }
+
+ /*
+ * Overridden to make public.
+ */
+ @Override
+ public final boolean processArgs(String[] args) {
+ return super.processArgs(args);
+ }
+
+ @Override
+ protected abstract String getName();
+}
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index 17b1042..997fb74 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -116,7 +116,8 @@
return true;
}
}
- static class ArgProcessor extends Link.ArgProcessor {
+
+ static class ArgProcessor extends CompileArgProcessor {
public ArgProcessor(CompilePermsOptions options) {
super(options);
registerHandler(new ArgHandlerPerms(options));
@@ -129,7 +130,7 @@
}
/**
- * Concrete class to implement all compiler options.
+ * Concrete class to implement compiler perm options.
*/
static class CompilePermsOptionsImpl extends CompileTaskOptionsImpl implements
CompilePermsOptions {
diff --git a/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java b/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java
index b2f0295..9c093c0 100644
--- a/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java
@@ -24,14 +24,13 @@
*/
class CompileTaskOptionsImpl implements CompileTaskOptions {
- public static final String GWT_COMPILER_DIR = ".gwt-tmp" + File.separatorChar
- + "compiler";
+ public static final String GWT_TMP_DIR = "gwt-tmp";
- private File compilerWorkDir;
private Type logLevel;
private String moduleName;
private File outDir;
private boolean useGuiLogger;
+ private File workDir;
public CompileTaskOptionsImpl() {
}
@@ -48,11 +47,7 @@
}
public File getCompilerWorkDir() {
- if (compilerWorkDir == null) {
- compilerWorkDir = new File(getOutDir(), GWT_COMPILER_DIR + File.separator
- + moduleName);
- }
- return compilerWorkDir;
+ return new File(new File(getWorkDir(), getModuleName()), "compiler");
}
public Type getLogLevel() {
@@ -87,4 +82,14 @@
this.useGuiLogger = useGuiLogger;
}
-}
\ No newline at end of file
+ /**
+ * TODO: add a command line option to pass files between compile phases?
+ */
+ protected File getWorkDir() {
+ if (workDir == null) {
+ workDir = new File(System.getProperty("java.io.tmpdir"), GWT_TMP_DIR);
+ workDir.mkdirs();
+ }
+ return workDir;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/CompilerOptions.java b/dev/core/src/com/google/gwt/dev/CompilerOptions.java
index 85b4afc..0b568e4 100644
--- a/dev/core/src/com/google/gwt/dev/CompilerOptions.java
+++ b/dev/core/src/com/google/gwt/dev/CompilerOptions.java
@@ -15,13 +15,11 @@
*/
package com.google.gwt.dev;
-import com.google.gwt.dev.jjs.JJSOptions;
-import com.google.gwt.dev.util.arg.OptionGenDir;
-import com.google.gwt.dev.util.arg.OptionValidateOnly;
+import com.google.gwt.dev.Link.LinkOptions;
+import com.google.gwt.dev.Precompile.PrecompileOptions;
/**
* The complete set of options for the GWT compiler.
*/
-public interface CompilerOptions extends JJSOptions, CompileTaskOptions,
- OptionGenDir, OptionValidateOnly {
+public interface CompilerOptions extends PrecompileOptions, LinkOptions {
}
diff --git a/dev/core/src/com/google/gwt/dev/CompilerOptionsImpl.java b/dev/core/src/com/google/gwt/dev/CompilerOptionsImpl.java
deleted file mode 100644
index dcc29bf..0000000
--- a/dev/core/src/com/google/gwt/dev/CompilerOptionsImpl.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2008 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.dev;
-
-import com.google.gwt.core.ext.TreeLogger.Type;
-import com.google.gwt.dev.jjs.JJSOptionsImpl;
-
-import java.io.File;
-
-/**
- * Concrete class to implement all compiler options.
- */
-public class CompilerOptionsImpl extends JJSOptionsImpl implements
- CompilerOptions {
- private File genDir;
- private Type logLevel;
- private String moduleName;
- private File outDir;
- private boolean useGuiLogger;
- private boolean validateOnly;
-
- public CompilerOptionsImpl() {
- }
-
- public CompilerOptionsImpl(CompilerOptions other) {
- copyFrom(other);
- }
-
- public void copyFrom(CompilerOptions other) {
- super.copyFrom(other);
- setGenDir(other.getGenDir());
- setLogLevel(other.getLogLevel());
- setOutDir(other.getOutDir());
- setUseGuiLogger(other.isUseGuiLogger());
- setValidateOnly(false);
- }
-
- public File getGenDir() {
- return genDir;
- }
-
- public Type getLogLevel() {
- return logLevel;
- }
-
- public String getModuleName() {
- return moduleName;
- }
-
- public File getOutDir() {
- return outDir;
- }
-
- public boolean isUseGuiLogger() {
- return useGuiLogger;
- }
-
- public boolean isValidateOnly() {
- return validateOnly;
- }
-
- public void setGenDir(File genDir) {
- this.genDir = genDir;
- }
-
- public void setLogLevel(Type logLevel) {
- this.logLevel = logLevel;
- }
-
- public void setModuleName(String moduleName) {
- this.moduleName = moduleName;
- }
-
- public void setOutDir(File outDir) {
- this.outDir = outDir;
- }
-
- public void setUseGuiLogger(boolean useGuiLogger) {
- this.useGuiLogger = useGuiLogger;
- }
-
- public void setValidateOnly(boolean validateOnly) {
- this.validateOnly = validateOnly;
- }
-}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index 3f89bfd..9e90603 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -19,7 +19,10 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.CompilePerms.CompilePermsOptionsImpl;
import com.google.gwt.dev.CompileTaskRunner.CompileTask;
-import com.google.gwt.dev.Precompile.CompilerOptionsImpl;
+import com.google.gwt.dev.Precompile.PrecompileOptionsImpl;
+import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
+
+import java.io.File;
/**
* The main executable entry point for the GWT Java to JavaScript compiler.
@@ -29,6 +32,7 @@
static final class ArgProcessor extends Precompile.ArgProcessor {
public ArgProcessor(CompilerOptions options) {
super(options);
+ registerHandler(new ArgHandlerExtraDir(options));
}
@Override
@@ -37,6 +41,32 @@
}
}
+ static class GWTCompilerOptionsImpl extends PrecompileOptionsImpl implements
+ CompilerOptions {
+
+ private File extraDir;
+
+ public GWTCompilerOptionsImpl() {
+ }
+
+ public GWTCompilerOptionsImpl(CompilerOptions other) {
+ copyFrom(other);
+ }
+
+ public void copyFrom(CompilerOptions other) {
+ super.copyFrom(other);
+ setExtraDir(other.getExtraDir());
+ }
+
+ public File getExtraDir() {
+ return extraDir;
+ }
+
+ public void setExtraDir(File extraDir) {
+ this.extraDir = extraDir;
+ }
+ }
+
public static void main(String[] args) {
/*
* NOTE: main always exits with a call to System.exit to terminate any
@@ -44,7 +74,7 @@
* shutdown AWT related threads, since the contract for their termination is
* still implementation-dependent.
*/
- final CompilerOptions options = new CompilerOptionsImpl();
+ final CompilerOptions options = new GWTCompilerOptionsImpl();
if (new ArgProcessor(options).processArgs(args)) {
CompileTask task = new CompileTask() {
public boolean run(TreeLogger logger) throws UnableToCompleteException {
@@ -60,10 +90,10 @@
System.exit(1);
}
- private final CompilerOptionsImpl options;
+ private final GWTCompilerOptionsImpl options;
public GWTCompiler(CompilerOptions options) {
- this.options = new CompilerOptionsImpl(options);
+ this.options = new GWTCompilerOptionsImpl(options);
}
public boolean run(TreeLogger logger) throws UnableToCompleteException {
diff --git a/dev/core/src/com/google/gwt/dev/GWTHosted.java b/dev/core/src/com/google/gwt/dev/GWTHosted.java
new file mode 100644
index 0000000..c8da35e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/GWTHosted.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2008 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.dev;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.shell.GWTShellServletFilter;
+import com.google.gwt.dev.shell.ServletContainer;
+import com.google.gwt.dev.shell.ShellModuleSpaceHost;
+import com.google.gwt.dev.shell.jetty.JettyLauncher;
+import com.google.gwt.dev.util.PerfLogger;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.util.tools.ArgHandlerExtra;
+import com.google.gwt.util.tools.ArgHandlerString;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The main executable class for the hosted mode shell.
+ */
+public class GWTHosted extends GWTShell {
+
+ /**
+ * Handles the set of modules that can be passed at the end of the command
+ * line.
+ */
+ protected class ArgHandlerModulesExtra extends ArgHandlerExtra {
+
+ private final PrintWriterTreeLogger console = new PrintWriterTreeLogger(
+ new PrintWriter(System.err));
+ {
+ console.setMaxDetail(TreeLogger.WARN);
+ }
+
+ @Override
+ public boolean addExtraArg(String arg) {
+ return addModule(console, arg);
+ }
+
+ @Override
+ public String getPurpose() {
+ return "Specifies the set of modules to host";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"module"};
+ }
+ }
+
+ /**
+ * Handles a startup url that can be passed on the command line.
+ */
+ protected class ArgHandlerStartupURLs extends ArgHandlerString {
+
+ @Override
+ public String getPurpose() {
+ return "Automatically launches the specified URL";
+ }
+
+ @Override
+ public String getTag() {
+ return "-startupUrl";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"url"};
+ }
+
+ @Override
+ public boolean setString(String arg) {
+ addStartupURL(arg);
+ return true;
+ }
+ }
+
+ public static void main(String[] args) {
+ /*
+ * NOTE: main always exits with a call to System.exit to terminate any
+ * non-daemon threads that were started in Generators. Typically, this is to
+ * shutdown AWT related threads, since the contract for their termination is
+ * still implementation-dependent.
+ */
+ BootStrapPlatform.init();
+ GWTHosted shellMain = new GWTHosted();
+ if (shellMain.processArgs(args)) {
+ shellMain.run();
+ }
+ System.exit(0);
+ }
+
+ private Set<ModuleDef> modules = new HashSet<ModuleDef>();
+
+ private ServletContainer server;
+
+ private GWTShellServletFilter servletFilter;
+
+ public GWTHosted() {
+ super(false, true);
+ registerHandler(new ArgHandlerStartupURLs());
+ registerHandler(new ArgHandlerModulesExtra());
+ }
+
+ public boolean addModule(TreeLogger logger, String moduleName) {
+ try {
+ ModuleDef moduleDef = ModuleDefLoader.loadFromClassPath(logger,
+ moduleName);
+ modules.add(moduleDef);
+ return true;
+ } catch (UnableToCompleteException e) {
+ System.err.println("Unable to load module '" + moduleName + "'");
+ return false;
+ }
+ }
+
+ @Override
+ protected ShellModuleSpaceHost doCreateShellModuleSpaceHost(
+ TreeLogger logger, TypeOracle typeOracle, ModuleDef moduleDef) {
+ return new ShellModuleSpaceHost(logger, typeOracle, moduleDef,
+ options.getGenDir(), options.getShellWorkDir(moduleDef), servletFilter);
+ }
+
+ @Override
+ protected void shutDown() {
+ if (server != null) {
+ try {
+ server.stop();
+ } catch (UnableToCompleteException e) {
+ // Already logged.
+ }
+ server = null;
+ }
+ }
+
+ @Override
+ protected int startUpServer() {
+ PerfLogger.start("GWTShell.startup (Jetty launch)");
+ JettyLauncher launcher = new JettyLauncher();
+ try {
+ TreeLogger serverLogger = getTopLogger().branch(TreeLogger.INFO,
+ "Starting HTTP on port " + getPort(), null);
+ ModuleDef[] moduleArray = modules.toArray(new ModuleDef[modules.size()]);
+ for (ModuleDef moduleDef : moduleArray) {
+ String[] servletPaths = moduleDef.getServletPaths();
+ if (servletPaths.length > 0) {
+ serverLogger.log(TreeLogger.WARN,
+ "Ignoring legacy <servlet> tag(s) in module '"
+ + moduleDef.getName()
+ + "'; add servlet tags to your web.xml instead");
+ }
+ }
+ servletFilter = new GWTShellServletFilter(serverLogger, options,
+ moduleArray);
+ server = launcher.start(serverLogger, getPort(), options.getOutDir(),
+ servletFilter);
+ } catch (UnableToCompleteException e) {
+ PerfLogger.end();
+ return -1;
+ }
+ assert (server != null);
+
+ PerfLogger.end();
+ return server.getPort();
+ }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/GWTShell.java b/dev/core/src/com/google/gwt/dev/GWTShell.java
index 7a56d3d..24ad277 100644
--- a/dev/core/src/com/google/gwt/dev/GWTShell.java
+++ b/dev/core/src/com/google/gwt/dev/GWTShell.java
@@ -19,7 +19,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.dev.Precompile.CompilerOptionsImpl;
+import com.google.gwt.dev.GWTCompiler.GWTCompilerOptionsImpl;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.shell.BrowserWidget;
@@ -30,10 +30,11 @@
import com.google.gwt.dev.shell.PlatformSpecific;
import com.google.gwt.dev.shell.ShellMainWindow;
import com.google.gwt.dev.shell.ShellModuleSpaceHost;
+import com.google.gwt.dev.shell.WorkDirs;
import com.google.gwt.dev.shell.tomcat.EmbeddedTomcatServer;
-import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
import com.google.gwt.dev.util.arg.ArgHandlerEnableAssertions;
+import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
@@ -160,9 +161,10 @@
}
/**
- * Handles the list of startup urls that can be passed on the command line.
+ * Handles the list of startup urls that can be passed at the end of the
+ * command line.
*/
- protected class ArgHandlerStartupURLs extends ArgHandlerExtra {
+ protected class ArgHandlerStartupURLsExtra extends ArgHandlerExtra {
@Override
public boolean addExtraArg(String arg) {
@@ -212,6 +214,20 @@
}
}
+ /**
+ * Concrete class to implement all compiler options.
+ */
+ static class ShellOptionsImpl extends GWTCompilerOptionsImpl implements
+ ShellOptions, WorkDirs {
+ public File getCompilerOutputDir(ModuleDef moduleDef) {
+ return new File(getWorkDir(), moduleDef.getDeployTo());
+ }
+
+ public File getShellWorkDir(ModuleDef moduleDef) {
+ return new File(new File(getWorkDir(), moduleDef.getName()), "shell");
+ }
+ }
+
private class BrowserWidgetHostImpl implements BrowserWidgetHost {
public BrowserWidgetHostImpl() {
}
@@ -244,14 +260,9 @@
ModuleDef moduleDef = loadModule(moduleName, logger);
assert (moduleDef != null);
- // Create a sandbox for the module.
- //
- File shellDir = new File(options.getOutDir(), GWT_SHELL_PATH
- + File.separator + moduleName);
-
TypeOracle typeOracle = moduleDef.getTypeOracle(logger);
ShellModuleSpaceHost host = doCreateShellModuleSpaceHost(logger,
- typeOracle, moduleDef, options.getGenDir(), shellDir);
+ typeOracle, moduleDef);
return host;
} finally {
Cursor normalCursor = display.getSystemCursor(SWT.CURSOR_ARROW);
@@ -291,9 +302,6 @@
}
}
- public static final String GWT_SHELL_PATH = ".gwt-tmp" + File.separator
- + "shell";
-
private static Image[] icons;
static {
@@ -314,7 +322,7 @@
}
public static String computeHostRegex(String url) {
- // the enture URL up to the first slash not prefixed by a slash or colon.
+ // the entire URL up to the first slash not prefixed by a slash or colon.
String raw = url.split("(?<![:/])/")[0];
// escape the dots and put a begin line specifier on the result
return "^" + raw.replaceAll("[.]", "[.]");
@@ -365,6 +373,8 @@
*/
protected final Display display = Display.getDefault();
+ protected final ShellOptionsImpl options = new ShellOptionsImpl();
+
/**
* Cheat on the first load's refresh by assuming the module loaded by
* {@link com.google.gwt.dev.shell.GWTShellServlet} is still fresh. This
@@ -381,8 +391,6 @@
private ShellMainWindow mainWnd;
- private final CompilerOptionsImpl options = new CompilerOptionsImpl();
-
private int port;
private boolean runTomcat = true;
@@ -408,17 +416,16 @@
registerHandler(new ArgHandlerLogLevel(options));
registerHandler(new ArgHandlerGenDir(options));
+ registerHandler(new ArgHandlerExtraDir(options));
if (!noURLs) {
- registerHandler(new ArgHandlerStartupURLs());
+ registerHandler(new ArgHandlerStartupURLsExtra());
}
registerHandler(new ArgHandlerOutDir(options));
registerHandler(new ArgHandlerScriptStyle(options));
-
registerHandler(new ArgHandlerEnableAssertions(options));
-
registerHandler(new ArgHandlerDisableAggressiveOptimization(options));
}
@@ -433,7 +440,7 @@
}
public CompilerOptions getCompilerOptions() {
- return new CompilerOptionsImpl(options);
+ return new GWTCompilerOptionsImpl(options);
}
public int getPort() {
@@ -578,7 +585,7 @@
*/
protected void compile(TreeLogger logger, ModuleDef moduleDef)
throws UnableToCompleteException {
- CompilerOptions newOptions = new CompilerOptionsImpl(options);
+ CompilerOptions newOptions = new GWTCompilerOptionsImpl(options);
newOptions.setModuleName(moduleDef.getName());
new GWTCompiler(newOptions).run(logger);
}
@@ -595,10 +602,9 @@
* @return ShellModuleSpaceHost instance
*/
protected ShellModuleSpaceHost doCreateShellModuleSpaceHost(
- TreeLogger logger, TypeOracle typeOracle, ModuleDef moduleDef,
- File genDir, File shellDir) {
- return new ShellModuleSpaceHost(logger, typeOracle, moduleDef, genDir,
- shellDir);
+ TreeLogger logger, TypeOracle typeOracle, ModuleDef moduleDef) {
+ return new ShellModuleSpaceHost(logger, typeOracle, moduleDef,
+ options.getGenDir(), options.getShellWorkDir(moduleDef), null);
}
/**
@@ -712,29 +718,30 @@
initializeLogger();
if (runTomcat) {
- // Start the HTTP server.
- // Use a new thread so that logging that occurs during startup is
- // displayed immediately.
- //
- final int serverPort = getPort();
-
- PerfLogger.start("GWTShell.startup (Tomcat launch)");
- String whyFailed = EmbeddedTomcatServer.start(getTopLogger(), serverPort,
- options.getOutDir());
- PerfLogger.end();
-
- if (whyFailed != null) {
- System.err.println(whyFailed);
+ int resultPort = startUpServer();
+ if (resultPort < 0) {
return false;
}
-
- // Record what port Tomcat is actually running on.
- port = EmbeddedTomcatServer.getPort();
+ port = resultPort;
}
return true;
}
+ protected int startUpServer() {
+ // TODO(bruce): make tomcat work in terms of the modular launcher
+ String whyFailed = EmbeddedTomcatServer.start(getTopLogger(), getPort(),
+ options);
+
+ // TODO(bruce): test that we can remove this old approach in favor of
+ // a better, logger-based error reporting
+ if (whyFailed != null) {
+ System.err.println(whyFailed);
+ return -1;
+ }
+ return EmbeddedTomcatServer.getPort();
+ }
+
private Shell createTrackedBrowserShell() {
final Shell shell = new Shell(display);
FillLayout fillLayout = new FillLayout();
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java
index 2bc55cd..d458108 100644
--- a/dev/core/src/com/google/gwt/dev/Link.java
+++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -27,11 +27,8 @@
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.cfg.StaticPropertyOracle;
import com.google.gwt.dev.util.Util;
-import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
-import com.google.gwt.dev.util.arg.ArgHandlerModuleName;
-import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
-import com.google.gwt.dev.util.arg.ArgHandlerTreeLoggerFlag;
-import com.google.gwt.util.tools.ToolBase;
+import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
+import com.google.gwt.dev.util.arg.OptionExtraDir;
import java.io.File;
import java.util.HashMap;
@@ -42,21 +39,16 @@
* to compile, and a ready-to-compile AST.
*/
public class Link {
+ /**
+ * Options for Link.
+ */
+ public interface LinkOptions extends CompileTaskOptions, OptionExtraDir {
+ }
- static class ArgProcessor extends ToolBase {
- public ArgProcessor(CompileTaskOptions options) {
- registerHandler(new ArgHandlerLogLevel(options));
- registerHandler(new ArgHandlerTreeLoggerFlag(options));
- registerHandler(new ArgHandlerOutDir(options));
- registerHandler(new ArgHandlerModuleName(options));
- }
-
- /*
- * Overridden to make public.
- */
- @Override
- public boolean processArgs(String[] args) {
- return super.processArgs(args);
+ static class ArgProcessor extends CompileArgProcessor {
+ public ArgProcessor(LinkOptions options) {
+ super(options);
+ registerHandler(new ArgHandlerExtraDir(options));
}
@Override
@@ -65,6 +57,35 @@
}
}
+ /**
+ * Concrete class to implement link options.
+ */
+ static class LinkOptionsImpl extends CompileTaskOptionsImpl implements
+ LinkOptions {
+
+ private File extraDir;
+
+ public LinkOptionsImpl() {
+ }
+
+ public LinkOptionsImpl(LinkOptions other) {
+ copyFrom(other);
+ }
+
+ public void copyFrom(LinkOptions other) {
+ super.copyFrom(other);
+ setExtraDir(other.getExtraDir());
+ }
+
+ public File getExtraDir() {
+ return extraDir;
+ }
+
+ public void setExtraDir(File extraDir) {
+ this.extraDir = extraDir;
+ }
+ }
+
public static ArtifactSet link(TreeLogger logger, ModuleDef module,
Precompilation precompilation, File[] jsFiles)
throws UnableToCompleteException {
@@ -80,7 +101,7 @@
* shutdown AWT related threads, since the contract for their termination is
* still implementation-dependent.
*/
- final CompileTaskOptions options = new CompileTaskOptionsImpl();
+ final LinkOptions options = new LinkOptionsImpl();
if (new ArgProcessor(options).processArgs(args)) {
CompileTask task = new CompileTask() {
public boolean run(TreeLogger logger) throws UnableToCompleteException {
@@ -110,7 +131,7 @@
}
linkerContext.addOrReplaceArtifacts(precompilation.getGeneratedArtifacts());
- return linkerContext.invokeLinkerStack(logger);
+ return linkerContext.invokeLink(logger);
}
private static void finishPermuation(TreeLogger logger, Permutation perm,
@@ -143,17 +164,17 @@
/**
* This is the output directory for private files.
*/
- private File moduleAuxDir;
+ private File moduleExtraDir;
/**
* This is the output directory for public files.
*/
private File moduleOutDir;
- private final CompileTaskOptionsImpl options;
+ private final LinkOptionsImpl options;
- public Link(CompileTaskOptions options) {
- this.options = new CompileTaskOptionsImpl(options);
+ public Link(LinkOptions options) {
+ this.options = new LinkOptionsImpl(options);
}
public boolean run(TreeLogger logger) throws UnableToCompleteException {
@@ -197,7 +218,7 @@
jsFiles);
if (artifacts != null) {
linkerContext.produceOutputDirectory(branch, artifacts, moduleOutDir,
- moduleAuxDir);
+ moduleExtraDir);
branch.log(TreeLogger.INFO, "Link succeeded");
return true;
}
@@ -207,9 +228,11 @@
private void init(TreeLogger logger) throws UnableToCompleteException {
module = ModuleDefLoader.loadFromClassPath(logger, options.getModuleName());
- moduleOutDir = new File(options.getOutDir(), module.getName());
+ moduleOutDir = new File(options.getOutDir(), module.getDeployTo());
Util.recursiveDelete(moduleOutDir, true);
- moduleAuxDir = new File(options.getOutDir(), module.getName() + "-aux");
- Util.recursiveDelete(moduleAuxDir, false);
+ if (options.getExtraDir() != null) {
+ moduleExtraDir = new File(options.getExtraDir(), module.getDeployTo());
+ Util.recursiveDelete(moduleExtraDir, false);
+ }
}
}
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index 8465ac5..0dd403a 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -30,11 +30,11 @@
import com.google.gwt.dev.jdt.RebindOracle;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
-import com.google.gwt.dev.jjs.UnifiedAst;
import com.google.gwt.dev.jjs.JJSOptions;
import com.google.gwt.dev.jjs.JJSOptionsImpl;
import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
import com.google.gwt.dev.jjs.JsOutputOption;
+import com.google.gwt.dev.jjs.UnifiedAst;
import com.google.gwt.dev.shell.StandardRebindOracle;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
@@ -42,6 +42,8 @@
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle;
import com.google.gwt.dev.util.arg.ArgHandlerValidateOnlyFlag;
+import com.google.gwt.dev.util.arg.OptionGenDir;
+import com.google.gwt.dev.util.arg.OptionValidateOnly;
import java.io.File;
import java.util.HashSet;
@@ -56,8 +58,15 @@
*/
public class Precompile {
- static class ArgProcessor extends Link.ArgProcessor {
- public ArgProcessor(CompilerOptions options) {
+ /**
+ * The set of options for the precompiler.
+ */
+ public interface PrecompileOptions extends JJSOptions, CompileTaskOptions,
+ OptionGenDir, OptionValidateOnly {
+ }
+
+ static class ArgProcessor extends CompileArgProcessor {
+ public ArgProcessor(PrecompileOptions options) {
super(options);
registerHandler(new ArgHandlerGenDir(options));
registerHandler(new ArgHandlerScriptStyle(options));
@@ -71,32 +80,27 @@
return Precompile.class.getName();
}
}
- /**
- * Concrete class to implement all compiler options.
- */
- static class CompilerOptionsImpl extends CompileTaskOptionsImpl implements
- CompilerOptions {
+ static class PrecompileOptionsImpl extends CompileTaskOptionsImpl implements
+ PrecompileOptions {
private File genDir;
private final JJSOptionsImpl jjsOptions = new JJSOptionsImpl();
private boolean validateOnly;
- public CompilerOptionsImpl() {
+ public PrecompileOptionsImpl() {
}
- public CompilerOptionsImpl(CompilerOptions other) {
+ public PrecompileOptionsImpl(PrecompileOptions other) {
copyFrom(other);
}
- public void copyFrom(CompilerOptions other) {
+ public void copyFrom(PrecompileOptions other) {
super.copyFrom(other);
+ jjsOptions.copyFrom(other);
+
setGenDir(other.getGenDir());
setValidateOnly(other.isValidateOnly());
-
- setAggressivelyOptimize(other.isAggressivelyOptimize());
- setEnableAssertions(other.isEnableAssertions());
- setOutput(other.getOutput());
}
public File getGenDir() {
@@ -217,7 +221,7 @@
* shutdown AWT related threads, since the contract for their termination is
* still implementation-dependent.
*/
- final CompilerOptions options = new CompilerOptionsImpl();
+ final PrecompileOptions options = new PrecompileOptionsImpl();
if (new ArgProcessor(options).processArgs(args)) {
CompileTask task = new CompileTask() {
public boolean run(TreeLogger logger) throws UnableToCompleteException {
@@ -328,14 +332,12 @@
}
}
- private File generatorResourcesDir;
-
private ModuleDef module;
- private final CompilerOptionsImpl options;
+ private final PrecompileOptionsImpl options;
- public Precompile(CompilerOptions options) {
- this.options = new CompilerOptionsImpl(options);
+ public Precompile(PrecompileOptions options) {
+ this.options = new PrecompileOptionsImpl(options);
}
public boolean run(TreeLogger logger) throws UnableToCompleteException {
@@ -344,7 +346,7 @@
TreeLogger branch = logger.branch(TreeLogger.INFO,
"Validating compilation " + module.getName());
if (validate(branch, options, module, options.getGenDir(),
- generatorResourcesDir)) {
+ options.getCompilerWorkDir())) {
branch.log(TreeLogger.INFO, "Validation succeeded");
return true;
} else {
@@ -356,7 +358,7 @@
TreeLogger branch = logger.branch(TreeLogger.INFO, "Precompiling module "
+ module.getName());
Precompilation precompilation = precompile(branch, options, module,
- options.getGenDir(), generatorResourcesDir);
+ options.getGenDir(), options.getCompilerWorkDir());
if (precompilation != null) {
Util.writeObjectAsFile(branch, new File(options.getCompilerWorkDir(),
PRECOMPILATION_FILENAME), precompilation);
@@ -382,10 +384,6 @@
this.module = ModuleDefLoader.loadFromClassPath(logger,
options.getModuleName());
- // Place generated resources inside the work dir.
- generatorResourcesDir = new File(compilerWorkDir, "generated");
- generatorResourcesDir.mkdirs();
-
// TODO: All JDT checks now before even building TypeOracle?
module.getCompilationState().compile(logger);
}
diff --git a/dev/core/src/com/google/gwt/dev/ShellOptions.java b/dev/core/src/com/google/gwt/dev/ShellOptions.java
new file mode 100644
index 0000000..71ea4e8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/ShellOptions.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2008 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.dev;
+
+import com.google.gwt.dev.jjs.JJSOptions;
+import com.google.gwt.dev.util.arg.OptionExtraDir;
+import com.google.gwt.dev.util.arg.OptionGenDir;
+import com.google.gwt.dev.util.arg.OptionLogLevel;
+import com.google.gwt.dev.util.arg.OptionOutDir;
+
+/**
+ * The complete set of options for the GWT compiler.
+ */
+public interface ShellOptions extends JJSOptions, OptionLogLevel, OptionOutDir,
+ OptionGenDir, OptionExtraDir {
+}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
index 3d6efb6..b4e4b1e 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -87,6 +87,8 @@
private CompilationState compilationState;
+ private String deployTo;
+
private final List<String> entryPointTypeNames = new ArrayList<String>();
private final Set<File> gwtXmlFiles = new HashSet<File>();
@@ -264,6 +266,17 @@
return compilationState;
}
+ /**
+ * Returns the desired deployment path within the output directory. The
+ * returned value will start and end with a <code>'/'</code> character.
+ */
+ public String getDeployTo() {
+ String result = (deployTo == null) ? ('/' + getName() + '/') : deployTo;
+ assert result.startsWith("/");
+ assert result.endsWith("/");
+ return result;
+ }
+
public synchronized String[] getEntryPointTypeNames() {
final int n = entryPointTypeNames.size();
return entryPointTypeNames.toArray(new String[n]);
@@ -368,6 +381,26 @@
}
/**
+ * Set the deployment path for this module. Setting this value to
+ * <code>null</code> or the empty string will default to the fully-qualified
+ * module name.
+ */
+ public void setDeployTo(String deployTo) {
+ if (deployTo != null && deployTo.length() == 0) {
+ deployTo = null;
+ } else {
+ assert deployTo.startsWith("/");
+ // Ensure ends with trailing slash.
+ if (!deployTo.endsWith("/")) {
+ deployTo += '/';
+ }
+ assert deployTo.endsWith("/");
+ }
+
+ this.deployTo = deployTo;
+ }
+
+ /**
* Override the module's apparent name. Setting this value to
* <code>null<code> will disable the name override.
*/
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
index a0c2e94..bd394eb 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -921,6 +921,7 @@
}
protected final String __module_1_rename_to = "";
+ protected final String __module_2_deploy_to = "";
private final PropertyAttrCvt bindingPropAttrCvt = new PropertyAttrCvt(
BindingProperty.class);
@@ -967,11 +968,21 @@
registerAttributeConverter(Class.class, classAttrCvt);
}
- protected Schema __module_begin(NullableName renameTo) {
+ protected Schema __module_begin(NullableName renameTo, String deployTo)
+ throws UnableToCompleteException {
+
+ if (deployTo != null && deployTo.length() > 0) {
+ // Only absolute paths, although it is okay to have multiple slashes.
+ if (!deployTo.startsWith("/")) {
+ logger.log(TreeLogger.ERROR, "deploy-to '" + deployTo
+ + "' must begin with forward slash (e.g. '/foo')");
+ throw new UnableToCompleteException();
+ }
+ }
return bodySchema;
}
- protected void __module_end(NullableName renameTo) {
+ protected void __module_end(NullableName renameTo, String deployTo) {
// Maybe infer source and public.
//
if (!foundExplicitSourceOrSuperSource) {
@@ -986,6 +997,7 @@
// We do this in __module_end so this value is never inherited
moduleDef.setNameOverride(renameTo.token);
+ moduleDef.setDeployTo(deployTo);
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java b/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java
index 670af09..8330b81 100644
--- a/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java
+++ b/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java
@@ -19,7 +19,6 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.impl.HostedModeLinker;
import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
-import com.google.gwt.dev.GWTShell;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.jjs.JJSOptionsImpl;
@@ -117,7 +116,7 @@
private int nextRequestId;
- private File outDir;
+ private WorkDirs workDirs;
private final Object requestIdLock = new Object();
@@ -427,8 +426,7 @@
if (foundResource == null) {
// Look for generated files
- File shellDir = new File(getOutputDir(), GWTShell.GWT_SHELL_PATH
- + File.separator + moduleName);
+ File shellDir = getShellWorkDirs().getShellWorkDir(moduleDef);
File requestedFile = new File(shellDir, partialPath);
if (requestedFile.exists()) {
try {
@@ -441,11 +439,10 @@
/*
* If the user is coming from compiled web-mode, check the linker output
- * directory for the real bootstrap file. We'll default to using the
- * output directory of the first linker defined in the <set-linker> tab.
+ * directory for the real bootstrap file.
*/
if (foundResource == null) {
- File moduleDir = new File(getOutputDir(), moduleName);
+ File moduleDir = getShellWorkDirs().getCompilerOutputDir(moduleDef);
File requestedFile = new File(moduleDir, partialPath);
if (requestedFile.exists()) {
try {
@@ -617,21 +614,21 @@
}
}
- private synchronized File getOutputDir() {
- if (outDir == null) {
- ServletContext servletContext = getServletContext();
- final String attr = "com.google.gwt.dev.shell.outdir";
- outDir = (File) servletContext.getAttribute(attr);
- assert (outDir != null);
- }
- return outDir;
- }
-
@SuppressWarnings("unchecked")
private Map<String, String[]> getParameterMap(HttpServletRequest request) {
return request.getParameterMap();
}
+ private synchronized WorkDirs getShellWorkDirs() {
+ if (workDirs == null) {
+ ServletContext servletContext = getServletContext();
+ final String attr = "com.google.gwt.dev.shell.workdirs";
+ workDirs = (WorkDirs) servletContext.getAttribute(attr);
+ assert (workDirs != null);
+ }
+ return workDirs;
+ }
+
private String guessMimeType(String fullPath) {
int dot = fullPath.lastIndexOf('.');
if (dot != -1) {
@@ -951,7 +948,7 @@
// ServeletContext.getResourceAsStream()
//
ServletContext context = new HostedModeServletContextProxy(
- getServletContext(), moduleDef, getOutputDir());
+ getServletContext(), moduleDef, getShellWorkDirs());
ServletConfig config = new HostedModeServletConfigProxy(
getServletConfig(), context);
diff --git a/dev/core/src/com/google/gwt/dev/shell/GWTShellServletFilter.java b/dev/core/src/com/google/gwt/dev/shell/GWTShellServletFilter.java
new file mode 100644
index 0000000..e8c599e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/GWTShellServletFilter.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2008 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.dev.shell;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
+import com.google.gwt.dev.ShellOptions;
+import com.google.gwt.dev.cfg.ModuleDef;
+
+import org.apache.commons.collections.map.AbstractReferenceMap;
+import org.apache.commons.collections.map.ReferenceIdentityMap;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Built-in servlet for convenient access to the public path of a specified
+ * module.
+ */
+public class GWTShellServletFilter implements Filter {
+
+ private final Map<String, ModuleDef> autogenScripts = new HashMap<String, ModuleDef>();
+ /**
+ * Maintains a persistent map of linker contexts for each module, for
+ * incremental re-link with new generated artifacts.
+ */
+ @SuppressWarnings("unchecked")
+ private final Map<ModuleDef, StandardLinkerContext> linkerContextsByModule = new ReferenceIdentityMap(
+ AbstractReferenceMap.WEAK, AbstractReferenceMap.HARD, true);
+
+ private TreeLogger logger;
+
+ private final ShellOptions options;
+
+ public GWTShellServletFilter(TreeLogger logger, ShellOptions options,
+ ModuleDef[] moduleDefs) {
+ this.logger = logger;
+ this.options = options;
+ for (ModuleDef moduleDef : moduleDefs) {
+ String scriptName = moduleDef.getDeployTo() + moduleDef.getName()
+ + ".nocache.js";
+ autogenScripts.put(scriptName, moduleDef);
+ }
+ }
+
+ public void destroy() {
+ }
+
+ public void doFilter(ServletRequest req, ServletResponse resp,
+ FilterChain chain) throws IOException, ServletException {
+
+ if (req instanceof HttpServletRequest) {
+ HttpServletRequest request = (HttpServletRequest) req;
+ String pathInfo = request.getRequestURI();
+ logger.log(TreeLogger.TRACE, "Request for: " + pathInfo);
+ ModuleDef moduleDef = autogenScripts.get(pathInfo);
+ if (moduleDef != null) {
+ /*
+ * If the '?compiled' request property is specified, don't
+ * auto-generate.
+ *
+ * TODO(scottb): does this even do anything anymore?
+ *
+ * TODO(scottb): how do we avoid clobbering a compiled selection script?
+ */
+ if (req.getParameter("compiled") == null) {
+ try {
+ // Run the linkers for hosted mode.
+ hostedModeLink(logger.branch(TreeLogger.TRACE, "Request for '"
+ + pathInfo + "' maps to script generator for module '"
+ + moduleDef.getName() + "'"), moduleDef);
+ } catch (UnableToCompleteException e) {
+ /*
+ * The error will have already been logged. Continue, since this
+ * could actually be a request for a static file that happens to
+ * have an unfortunately confusing name.
+ */
+ }
+ }
+ }
+ }
+
+ // Do normal handling, knowing that the linkers may have run earlier to
+ // produce files we are just about to serve.
+ chain.doFilter(req, resp);
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ /**
+ * Called when new generated artifacts are produced.
+ */
+ void relink(TreeLogger logger, ModuleDef moduleDef, ArtifactSet newArtifacts)
+ throws UnableToCompleteException {
+ StandardLinkerContext context = linkerContextsByModule.get(moduleDef);
+ assert context != null;
+
+ ArtifactSet artifacts = context.invokeRelink(logger, newArtifacts);
+ dumpArtifacts(logger, moduleDef, context, artifacts);
+ }
+
+ private void dumpArtifacts(TreeLogger logger, ModuleDef moduleDef,
+ StandardLinkerContext context, ArtifactSet artifacts)
+ throws UnableToCompleteException {
+ File outputPath = new File(options.getOutDir(), moduleDef.getDeployTo());
+ File extraPath = null;
+ if (options.getExtraDir() != null) {
+ extraPath = new File(options.getExtraDir(), moduleDef.getDeployTo());
+ }
+ context.produceOutputDirectory(logger, artifacts, outputPath, extraPath);
+ }
+
+ private void hostedModeLink(TreeLogger logger, ModuleDef moduleDef)
+ throws UnableToCompleteException {
+ String moduleName = moduleDef.getName();
+ logger.log(TreeLogger.TRACE, "Running linkers for module " + moduleName);
+
+ // TODO: blow away artifacts from a previous link.
+
+ // Perform the initial link.
+ StandardLinkerContext context = new StandardLinkerContext(logger,
+ moduleDef, options);
+ ArtifactSet artifacts = context.invokeLink(logger);
+ dumpArtifacts(logger, moduleDef, context, artifacts);
+
+ // Save off a new active link state (which may overwrite an old one).
+ linkerContextsByModule.put(moduleDef, context);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/HostedModeServletContextProxy.java b/dev/core/src/com/google/gwt/dev/shell/HostedModeServletContextProxy.java
index 5270bf1..852bc65 100644
--- a/dev/core/src/com/google/gwt/dev/shell/HostedModeServletContextProxy.java
+++ b/dev/core/src/com/google/gwt/dev/shell/HostedModeServletContextProxy.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.dev.shell;
-import com.google.gwt.dev.GWTShell;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.resource.Resource;
@@ -43,13 +42,13 @@
* Avoid pinning my moduleDef.
*/
private final WeakReference<ModuleDef> moduleDefRef;
- private final File outDir;
+ private final WorkDirs workDirs;
HostedModeServletContextProxy(ServletContext context, ModuleDef moduleDef,
- File outDir) {
+ WorkDirs workDirs) {
this.context = context;
this.moduleDefRef = new WeakReference<ModuleDef>(moduleDef);
- this.outDir = outDir;
+ this.workDirs = workDirs;
}
/**
@@ -78,6 +77,10 @@
return context.getContext(arg0);
}
+ public String getContextPath() {
+ return context.getContextPath();
+ }
+
/**
* @param arg0
* @return
@@ -179,8 +182,7 @@
}
// Otherwise try the path but rooted in the shell's output directory
- File shellDir = new File(outDir, GWTShell.GWT_SHELL_PATH + File.separator
- + moduleDef.getName());
+ File shellDir = workDirs.getShellWorkDir(moduleDef);
File requestedFile = new File(shellDir, partialPath);
if (requestedFile.exists()) {
return requestedFile.toURI().toURL();
@@ -191,7 +193,8 @@
* directory for the file. We'll default to using the output directory of
* the first linker defined in the <set-linker> tab.
*/
- requestedFile = new File(new File(outDir, moduleDef.getName()), partialPath);
+ File linkDir = workDirs.getCompilerOutputDir(moduleDef);
+ requestedFile = new File(linkDir, partialPath);
if (requestedFile.exists()) {
try {
return requestedFile.toURI().toURL();
diff --git a/dev/core/src/com/google/gwt/dev/shell/ServletContainer.java b/dev/core/src/com/google/gwt/dev/shell/ServletContainer.java
new file mode 100644
index 0000000..732998a
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/ServletContainer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2008 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.dev.shell;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+
+/**
+ * An instance of a servlet container that can be used by the shell. It is
+ * assumed that this servlet container serves a web app from the root directory
+ * specified by a call to
+ * {@link ServletContainerLauncher#setAppRootDir(java.io.File)}.
+ */
+public interface ServletContainer {
+
+ /**
+ * Provides the port on which the server is actually running, which can be
+ * useful when automatic port selection was requested.
+ */
+ int getPort();
+
+ /**
+ * Causes the web app to pick up changes made within the app root dir while
+ * running. This method cannot be called after {@link #stop()} has been
+ * called.
+ *
+ * TODO(bruce): need to determine whether all the important servlet containers
+ * will let us do this (e.g. ensure they don't lock files we would need to
+ * update)
+ *
+ * @throws UnableToCompleteException
+ */
+ void refresh() throws UnableToCompleteException;
+
+ /**
+ * Stops the running servlet container. It cannot be restarted after this.
+ *
+ * @throws UnableToCompleteException
+ */
+ void stop() throws UnableToCompleteException;
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ServletContainerLauncher.java b/dev/core/src/com/google/gwt/dev/shell/ServletContainerLauncher.java
new file mode 100644
index 0000000..e3896e2
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/ServletContainerLauncher.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 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.dev.shell;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+
+import java.io.File;
+
+import javax.servlet.Filter;
+
+/**
+ * Defines the service provider interface for launching servlet containers that
+ * can be used by the shell.
+ */
+public interface ServletContainerLauncher {
+
+ ServletContainer start(TreeLogger topLogger, int port, File appRootDir,
+ Filter shellServletFilter) throws UnableToCompleteException;
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
index 39cb165..311c564 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
@@ -21,7 +21,7 @@
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.Rules;
-import com.google.gwt.dev.jdt.RebindOracle;
+import com.google.gwt.dev.shell.StandardRebindOracle.ArtifactAcceptor;
import java.io.File;
@@ -41,7 +41,9 @@
private final ModuleDef module;
- private RebindOracle rebindOracle;
+ private StandardRebindOracle rebindOracle;
+
+ private final GWTShellServletFilter servletFilter;
private final File shellDir;
@@ -52,15 +54,14 @@
* @param saveJsni
*/
public ShellModuleSpaceHost(TreeLogger logger, TypeOracle typeOracle,
- ModuleDef module, File genDir, File shellDir) {
+ ModuleDef module, File genDir, File shellDir,
+ GWTShellServletFilter servletFilter) {
this.logger = logger;
this.typeOracle = typeOracle;
this.module = module;
this.genDir = genDir;
-
- // Combine the user's output dir with the module name to get the
- // module-specific output dir.
this.shellDir = shellDir;
+ this.servletFilter = servletFilter;
}
public CompilingClassLoader getClassLoader() {
@@ -108,10 +109,19 @@
module.getCompilationState(), readySpace);
}
- public String rebind(TreeLogger rebindLogger, String sourceTypeName)
+ public String rebind(final TreeLogger rebindLogger, String sourceTypeName)
throws UnableToCompleteException {
checkForModuleSpace();
- return rebindOracle.rebind(rebindLogger, sourceTypeName);
+
+ ArtifactAcceptor artifactAcceptor = (servletFilter == null) ? null
+ : new ArtifactAcceptor() {
+ public void accept(ArtifactSet newlyGeneratedArtifacts)
+ throws UnableToCompleteException {
+ servletFilter.relink(rebindLogger, module, newlyGeneratedArtifacts);
+ }
+ };
+
+ return rebindOracle.rebind(rebindLogger, sourceTypeName, artifactAcceptor);
}
private void checkForModuleSpace() {
diff --git a/dev/core/src/com/google/gwt/dev/shell/StandardGeneratorContext.java b/dev/core/src/com/google/gwt/dev/shell/StandardGeneratorContext.java
index 332f7de..64687f5 100644
--- a/dev/core/src/com/google/gwt/dev/shell/StandardGeneratorContext.java
+++ b/dev/core/src/com/google/gwt/dev/shell/StandardGeneratorContext.java
@@ -25,7 +25,6 @@
import com.google.gwt.core.ext.linker.GeneratedResource;
import com.google.gwt.core.ext.linker.impl.StandardGeneratedResource;
import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.cfg.PublicOracle;
import com.google.gwt.dev.javac.CompilationState;
@@ -175,7 +174,7 @@
}
}
- private final ArtifactSet artifactSet;
+ private final ArtifactSet allGeneratedArtifacts;
private final Set<GeneratedUnitWithFile> committedGeneratedCups = new HashSet<GeneratedUnitWithFile>();
@@ -185,9 +184,11 @@
private final File genDir;
- private final Set<String> generatedTypeNames = new HashSet<String>();
+ private final File generatorResourcesDir;
- private final File outDir;
+ private ArtifactSet newlyGeneratedArtifacts = new ArtifactSet();
+
+ private final Set<String> newlyGeneratedTypeNames = new HashSet<String>();
private final Map<OutputStream, PendingResource> pendingResourcesByOutputStream = new IdentityHashMap<OutputStream, PendingResource>();
@@ -203,13 +204,13 @@
*/
public StandardGeneratorContext(CompilationState compilationState,
PropertyOracle propOracle, PublicOracle publicOracle, File genDir,
- File outDir, ArtifactSet artifactSet) {
+ File generatorResourcesDir, ArtifactSet allGeneratedArtifacts) {
this.compilationState = compilationState;
this.propOracle = propOracle;
this.publicOracle = publicOracle;
this.genDir = genDir;
- this.outDir = outDir;
- this.artifactSet = artifactSet;
+ this.generatorResourcesDir = generatorResourcesDir;
+ this.allGeneratedArtifacts = allGeneratedArtifacts;
}
/**
@@ -233,8 +234,8 @@
*/
public void commitArtifact(TreeLogger logger, Artifact<?> artifact)
throws UnableToCompleteException {
- // The artifactSet will be null in hosted mode, since we never run Linkers
- artifactSet.replace(artifact);
+ allGeneratedArtifacts.replace(artifact);
+ newlyGeneratedArtifacts.add(artifact);
}
public GeneratedResource commitResource(TreeLogger logger, OutputStream os)
@@ -279,9 +280,9 @@
* uncommitted compilation units and to force committed compilation units to
* be parsed and added to the type oracle.
*
- * @return types generated during this object's lifetime
+ * @return any newly generated artifacts since the last call
*/
- public final JClassType[] finish(TreeLogger logger)
+ public final ArtifactSet finish(TreeLogger logger)
throws UnableToCompleteException {
abortUncommittedResources(logger);
@@ -317,21 +318,16 @@
compilationState.compile(logger);
}
- // Return the generated types.
+ // Make sure all generated types can be found in TypeOracle.
TypeOracle typeOracle = getTypeOracle();
- JClassType[] genTypes = new JClassType[genTypeNames.size()];
- int next = 0;
- for (Iterator<String> iter = genTypeNames.iterator(); iter.hasNext();) {
- String genTypeName = iter.next();
- try {
- genTypes[next++] = typeOracle.getType(genTypeName);
- } catch (NotFoundException e) {
+ for (String genTypeName : genTypeNames) {
+ if (typeOracle.findType(genTypeName) == null) {
String msg = "Unable to find recently-generated type '" + genTypeName;
logger.log(TreeLogger.ERROR, msg, null);
throw new UnableToCompleteException();
}
}
- return genTypes;
+ return newlyGeneratedArtifacts;
} finally {
// Remind the user if there uncommitted cups.
@@ -346,14 +342,11 @@
uncommittedGeneratedCupsByPrintWriter.clear();
committedGeneratedCups.clear();
- generatedTypeNames.clear();
+ newlyGeneratedTypeNames.clear();
+ newlyGeneratedArtifacts = new ArtifactSet();
}
}
- public File getOutputDir() {
- return outDir;
- }
-
public final PropertyOracle getPropertyOracle() {
return propOracle;
}
@@ -380,7 +373,7 @@
}
// Has anybody tried to create this type during this iteration?
- if (generatedTypeNames.contains(typeName)) {
+ if (newlyGeneratedTypeNames.contains(typeName)) {
final String msg = "A request to create type '"
+ typeName
+ "' was received while the type itself was being created; this might be a generator or configuration bug";
@@ -398,7 +391,7 @@
}
GeneratedUnitWithFile gcup = new GeneratedUnitWithFile(qualifiedSourceName);
uncommittedGeneratedCupsByPrintWriter.put(gcup.pw, gcup);
- generatedTypeNames.add(typeName);
+ newlyGeneratedTypeNames.add(typeName);
return gcup.pw;
}
@@ -443,7 +436,7 @@
}
// See if the file is already committed.
- SortedSet<GeneratedResource> resources = artifactSet.find(GeneratedResource.class);
+ SortedSet<GeneratedResource> resources = allGeneratedArtifacts.find(GeneratedResource.class);
for (GeneratedResource resource : resources) {
if (partialPath.equals(resource.getPartialPath())) {
return null;
@@ -462,7 +455,8 @@
}
// Record that this file is pending.
- PendingResource pendingResource = new PendingResource(outDir, partialPath);
+ PendingResource pendingResource = new PendingResource(
+ generatorResourcesDir, partialPath);
OutputStream os = pendingResource.getOutputStream();
pendingResourcesByOutputStream.put(os, pendingResource);
diff --git a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
index 3cf0c7f..94e7a88 100644
--- a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
@@ -41,6 +41,18 @@
public class StandardRebindOracle implements RebindOracle {
/**
+ * A call-back interface to be notified when new types are generated.
+ *
+ */
+ public interface ArtifactAcceptor {
+ /**
+ * Called if new artifacts are generated.
+ */
+ void accept(ArtifactSet newlyGeneratedArtifacts)
+ throws UnableToCompleteException;
+ }
+
+ /**
* Makes the actual deferred binding decision by examining rules.
*/
private final class Rebinder {
@@ -53,14 +65,17 @@
public Rebinder() {
genCtx = new StandardGeneratorContext(compilationState, propOracle,
- publicOracle, genDir, outDir, artifactSet);
+ publicOracle, genDir, generatorResourcesDir, allGeneratedArtifacts);
}
- public String rebind(TreeLogger logger, String typeName)
- throws UnableToCompleteException {
+ public String rebind(TreeLogger logger, String typeName,
+ ArtifactAcceptor artifactAcceptor) throws UnableToCompleteException {
String result = tryRebind(logger, typeName);
- genCtx.finish(logger);
+ ArtifactSet newlyGeneratedArtifacts = genCtx.finish(logger);
+ if (!newlyGeneratedArtifacts.isEmpty() && artifactAcceptor != null) {
+ artifactAcceptor.accept(newlyGeneratedArtifacts);
+ }
if (result == null) {
result = typeName;
}
@@ -125,7 +140,7 @@
}
}
- private final ArtifactSet artifactSet;
+ private final ArtifactSet allGeneratedArtifacts;
private final Map<String, String> cache = new HashMap<String, String>();
@@ -133,7 +148,7 @@
private final File genDir;
- private final File outDir;
+ private final File generatorResourcesDir;
private final PropertyOracle propOracle;
@@ -143,25 +158,30 @@
public StandardRebindOracle(CompilationState compilationState,
PropertyOracle propOracle, PublicOracle publicOracle, Rules rules,
- File genDir, File moduleOutDir, ArtifactSet artifactSet) {
+ File genDir, File generatorResourcesDir, ArtifactSet allGeneratedArtifacts) {
this.compilationState = compilationState;
this.propOracle = propOracle;
this.publicOracle = publicOracle;
this.rules = rules;
this.genDir = genDir;
- this.outDir = moduleOutDir;
- this.artifactSet = artifactSet;
+ this.generatorResourcesDir = generatorResourcesDir;
+ this.allGeneratedArtifacts = allGeneratedArtifacts;
}
public String rebind(TreeLogger logger, String typeName)
throws UnableToCompleteException {
+ return rebind(logger, typeName, null);
+ }
+
+ public String rebind(TreeLogger logger, String typeName,
+ ArtifactAcceptor artifactAcceptor) throws UnableToCompleteException {
String result = cache.get(typeName);
if (result == null) {
logger = Messages.TRACE_TOPLEVEL_REBIND.branch(logger, typeName, null);
Rebinder rebinder = new Rebinder();
- result = rebinder.rebind(logger, typeName);
+ result = rebinder.rebind(logger, typeName, artifactAcceptor);
cache.put(typeName, result);
Messages.TRACE_TOPLEVEL_REBIND_RESULT.log(logger, result, null);
diff --git a/dev/core/src/com/google/gwt/dev/shell/WorkDirs.java b/dev/core/src/com/google/gwt/dev/shell/WorkDirs.java
new file mode 100644
index 0000000..fd62396
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/WorkDirs.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008 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.dev.shell;
+
+import com.google.gwt.dev.cfg.ModuleDef;
+
+import java.io.File;
+
+/**
+ * Provides information about work directories.
+ */
+public interface WorkDirs {
+ /**
+ * Gets the compiler output directory for a particular module.
+ */
+ File getCompilerOutputDir(ModuleDef moduleDef);
+
+ /**
+ * Gets the shell work directory for a particular module.
+ */
+ File getShellWorkDir(ModuleDef moduleDef);
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
new file mode 100644
index 0000000..9a6b12b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2008 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.dev.shell.jetty;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.dev.shell.ServletContainer;
+import com.google.gwt.dev.shell.ServletContainerLauncher;
+
+import org.mortbay.jetty.Handler;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.servlet.FilterHolder;
+import org.mortbay.jetty.webapp.WebAppContext;
+import org.mortbay.log.Log;
+import org.mortbay.log.Logger;
+
+import java.io.File;
+
+import javax.servlet.Filter;
+
+/**
+ * A launcher for an embedded Jetty server.
+ */
+public class JettyLauncher implements ServletContainerLauncher {
+
+ /**
+ * An adapter for the Jetty logging system to GWT's TreeLogger. This
+ * implementation class is only public to allow {@link Log} to instantiate it.
+ *
+ * The weird static data / default construction setup is a game we play with
+ * {@link Log}'s static initializer to prevent the initial log message from
+ * going to stderr.
+ */
+ public static final class JettyTreeLogger implements Logger {
+ private static Type nextBranchLevel;
+ private static TreeLogger nextLogger;
+
+ /**
+ * Returns true if the default constructor can be called.
+ */
+ public static boolean isDefaultConstructionReady() {
+ return nextLogger != null;
+ }
+
+ /**
+ * Call to set initial state for default construction; must be called again
+ * each time before a default instantiation occurs.
+ */
+ public static void setDefaultConstruction(TreeLogger logger,
+ Type branchLevel) {
+ if (logger == null || branchLevel == null) {
+ throw new NullPointerException();
+ }
+ nextLogger = logger;
+ nextBranchLevel = branchLevel;
+ }
+
+ private final Type branchLevel;
+ private final TreeLogger logger;
+
+ public JettyTreeLogger() {
+ this(nextLogger, nextBranchLevel);
+ nextLogger = null;
+ nextBranchLevel = null;
+ }
+
+ public JettyTreeLogger(TreeLogger logger, Type branchLevel) {
+ if (logger == null || branchLevel == null) {
+ throw new NullPointerException();
+ }
+ this.branchLevel = branchLevel;
+ this.logger = logger;
+ }
+
+ public void debug(String msg, Object arg0, Object arg1) {
+ logger.log(TreeLogger.DEBUG, format(msg, arg0, arg1));
+ }
+
+ public void debug(String msg, Throwable th) {
+ logger.log(TreeLogger.DEBUG, msg, th);
+ }
+
+ public Logger getLogger(String name) {
+ return new JettyTreeLogger(logger.branch(branchLevel, name), branchLevel);
+ }
+
+ public void info(String msg, Object arg0, Object arg1) {
+ logger.log(TreeLogger.INFO, format(msg, arg0, arg1));
+ }
+
+ public boolean isDebugEnabled() {
+ return logger.isLoggable(TreeLogger.DEBUG);
+ }
+
+ public void setDebugEnabled(boolean enabled) {
+ // ignored
+ }
+
+ public void warn(String msg, Object arg0, Object arg1) {
+ logger.log(TreeLogger.WARN, format(msg, arg0, arg1));
+ }
+
+ public void warn(String msg, Throwable th) {
+ logger.log(TreeLogger.WARN, msg, th);
+ }
+
+ /**
+ * Copied from org.mortbay.log.StdErrLog.
+ */
+ private String format(String msg, Object arg0, Object arg1) {
+ int i0 = msg.indexOf("{}");
+ int i1 = i0 < 0 ? -1 : msg.indexOf("{}", i0 + 2);
+
+ if (arg1 != null && i1 >= 0) {
+ msg = msg.substring(0, i1) + arg1 + msg.substring(i1 + 2);
+ }
+ if (arg0 != null && i0 >= 0) {
+ msg = msg.substring(0, i0) + arg0 + msg.substring(i0 + 2);
+ }
+ return msg;
+ }
+ }
+
+ private static class JettyServletContainer implements ServletContainer {
+
+ private final int actualPort;
+ private final File appRootDir;
+ private final TreeLogger logger;
+ private final WebAppContext wac;
+
+ public JettyServletContainer(TreeLogger logger, WebAppContext wac,
+ int actualPort, File appRootDir) {
+ this.logger = logger;
+ this.wac = wac;
+ this.actualPort = actualPort;
+ this.appRootDir = appRootDir;
+ }
+
+ public int getPort() {
+ return actualPort;
+ }
+
+ public void refresh() throws UnableToCompleteException {
+ String msg = "Reloading web app to reflect changes in "
+ + appRootDir.getAbsolutePath();
+ TreeLogger branch = logger.branch(TreeLogger.INFO, msg);
+ try {
+ wac.stop();
+ } catch (Exception e) {
+ branch.log(TreeLogger.ERROR, "Unable to stop embedded Jetty server", e);
+ throw new UnableToCompleteException();
+ }
+
+ try {
+ wac.start();
+ } catch (Exception e) {
+ branch.log(TreeLogger.ERROR, "Unable to stop embedded Jetty server", e);
+ throw new UnableToCompleteException();
+ }
+
+ branch.log(TreeLogger.INFO, "Reload completed successfully");
+ }
+
+ public void stop() throws UnableToCompleteException {
+ TreeLogger branch = logger.branch(TreeLogger.INFO,
+ "Stopping Jetty server");
+ try {
+ wac.stop();
+ } catch (Exception e) {
+ branch.log(TreeLogger.ERROR, "Unable to stop embedded Jetty server", e);
+ throw new UnableToCompleteException();
+ }
+ branch.log(TreeLogger.INFO, "Stopped successfully");
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public ServletContainer start(TreeLogger logger, int port, File appRootDir,
+ Filter shellServletFilter) throws UnableToCompleteException {
+ checkStartParams(logger, port, appRootDir);
+
+ // The dance we do with Jetty's logging system.
+ System.setProperty("VERBOSE", "true");
+ JettyTreeLogger.setDefaultConstruction(logger, TreeLogger.INFO);
+ System.setProperty("org.mortbay.log.class", JettyTreeLogger.class.getName());
+ // Force initialization.
+ Log.isDebugEnabled();
+ if (JettyTreeLogger.isDefaultConstructionReady()) {
+ // The log system was already initialized and did not use our
+ // newly-constructed logger, set it explicitly now.
+ Log.setLog(new JettyTreeLogger());
+ }
+
+ Server server = new Server();
+ SelectChannelConnector connector = new SelectChannelConnector();
+ connector.setPort(port);
+ connector.setHost("127.0.0.1");
+ server.addConnector(connector);
+
+ // Create a new web app in the war directory.
+ WebAppContext wac = new WebAppContext(appRootDir.getAbsolutePath(), "/");
+
+ // Prevent file locking on windows; pick up file changes.
+ wac.getInitParams().put(
+ "org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false");
+
+ // Setup the shell servlet filter to generate nocache.js files (and run
+ // the hosted mode linker stack.
+ FilterHolder filterHolder = new FilterHolder();
+ filterHolder.setFilter(shellServletFilter);
+ wac.addFilter(filterHolder, "/*", Handler.ALL);
+
+ server.setHandler(wac);
+ server.setStopAtShutdown(true);
+
+ try {
+ server.start();
+ int actualPort = connector.getPort();
+ return new JettyServletContainer(logger, wac, actualPort, appRootDir);
+ } catch (Exception e) {
+ logger.log(TreeLogger.ERROR, "Unable to start embedded Jetty server", e);
+ throw new UnableToCompleteException();
+ }
+ }
+
+ private void checkStartParams(TreeLogger logger, int port, File appRootDir) {
+ if (logger == null) {
+ throw new NullPointerException("logger cannot be null");
+ }
+
+ if (port < 0 || port > 65535) {
+ throw new IllegalArgumentException(
+ "port must be either 0 (for auto) or less than 65536");
+ }
+
+ if (appRootDir == null) {
+ throw new NullPointerException("app root direcotry cannot be null");
+ }
+ }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java b/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
index 0ebbe02..1fc2d9b 100644
--- a/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
+++ b/dev/core/src/com/google/gwt/dev/shell/tomcat/EmbeddedTomcatServer.java
@@ -21,6 +21,7 @@
import com.google.gwt.dev.resource.impl.PathPrefix;
import com.google.gwt.dev.resource.impl.PathPrefixSet;
import com.google.gwt.dev.resource.impl.ResourceOracleImpl;
+import com.google.gwt.dev.shell.WorkDirs;
import com.google.gwt.util.tools.Utility;
import org.apache.catalina.Connector;
@@ -60,13 +61,13 @@
}
public static synchronized String start(TreeLogger topLogger, int port,
- File outDir) {
+ WorkDirs workDirs) {
if (sTomcat != null) {
throw new IllegalStateException("Embedded Tomcat is already running");
}
try {
- new EmbeddedTomcatServer(topLogger, port, outDir);
+ new EmbeddedTomcatServer(topLogger, port, workDirs);
return null;
} catch (LifecycleException e) {
String msg = e.getMessage();
@@ -145,7 +146,7 @@
private final TreeLogger startupBranchLogger;
private EmbeddedTomcatServer(final TreeLogger topLogger, int listeningPort,
- final File outDir) throws LifecycleException {
+ final WorkDirs workDirs) throws LifecycleException {
if (topLogger == null) {
throw new NullPointerException("No logger specified");
}
@@ -222,7 +223,7 @@
if (StandardHost.PRE_INSTALL_EVENT.equals(event.getType())) {
StandardContext webapp = (StandardContext) event.getData();
publishShellLoggerAttribute(logger, topLogger, webapp);
- publishShellOutDirAttribute(logger, outDir, webapp);
+ publishShellWorkDirsAttribute(logger, workDirs, webapp);
}
}
});
@@ -411,12 +412,12 @@
}
/**
- * Publish the shell's output dir as an attribute. This attribute is used to
+ * Publish the shell's work dir as an attribute. This attribute is used to
* find it out of the thin air within the shell servlet.
*/
- private void publishShellOutDirAttribute(TreeLogger logger,
- File outDirToPublish, StandardContext webapp) {
- final String attr = "com.google.gwt.dev.shell.outdir";
- publishAttributeToWebApp(logger, webapp, attr, outDirToPublish);
+ private void publishShellWorkDirsAttribute(TreeLogger logger,
+ WorkDirs workDirs, StandardContext webapp) {
+ final String attr = "com.google.gwt.dev.shell.workdirs";
+ publishAttributeToWebApp(logger, webapp, attr, workDirs);
}
}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerExtraDir.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerExtraDir.java
new file mode 100644
index 0000000..42626cf
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerExtraDir.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2008 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.dev.util.arg;
+
+import com.google.gwt.util.tools.ArgHandlerDir;
+
+import java.io.File;
+
+/**
+ * Argument handler for processing the extra directory option.
+ */
+public final class ArgHandlerExtraDir extends ArgHandlerDir {
+
+ private final OptionExtraDir option;
+
+ public ArgHandlerExtraDir(OptionExtraDir option) {
+ this.option = option;
+ }
+
+ public String getPurpose() {
+ return "The directory into which extra, non-deployed files will be written";
+ }
+
+ public String getTag() {
+ return "-extra";
+ }
+
+ @Override
+ public void setDir(File dir) {
+ option.setExtraDir(dir);
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionExtraDir.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionExtraDir.java
new file mode 100644
index 0000000..586f41f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionExtraDir.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2008 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.dev.util.arg;
+
+import java.io.File;
+
+/**
+ * Option to set the output directory for extra artifacts.
+ */
+public interface OptionExtraDir {
+
+ /**
+ * Returns the extra resource directory.
+ */
+ File getExtraDir();
+
+ /**
+ * Sets the extra resource directory.
+ */
+ void setExtraDir(File dir);
+}
diff --git a/eclipse/dev/linux/.classpath b/eclipse/dev/linux/.classpath
index 3cc771f..4b09998 100644
--- a/eclipse/dev/linux/.classpath
+++ b/eclipse/dev/linux/.classpath
@@ -9,6 +9,7 @@
<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar" sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip"/>
+ <classpathentry kind="var" path="GWT_TOOLS/lib/jetty/jetty-6.1.11.jar" sourcepath="/GWT_TOOLS/lib/jetty/jetty-6.1.11-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar" sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/ant-launcher-1.6.5.jar"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-1.0.jar"/>
diff --git a/eclipse/dev/mac/.classpath b/eclipse/dev/mac/.classpath
index f9ad860..3832d9a 100644
--- a/eclipse/dev/mac/.classpath
+++ b/eclipse/dev/mac/.classpath
@@ -9,6 +9,7 @@
<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar" sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip"/>
+ <classpathentry kind="var" path="GWT_TOOLS/lib/jetty/jetty-6.1.11.jar" sourcepath="/GWT_TOOLS/lib/jetty/jetty-6.1.11-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar" sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/ant-launcher-1.6.5.jar"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-1.0.jar"/>
diff --git a/eclipse/dev/windows/.classpath b/eclipse/dev/windows/.classpath
index ff4799f..70fc175 100644
--- a/eclipse/dev/windows/.classpath
+++ b/eclipse/dev/windows/.classpath
@@ -9,6 +9,7 @@
<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar" sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip"/>
+ <classpathentry kind="var" path="GWT_TOOLS/lib/jetty/jetty-6.1.11.jar" sourcepath="/GWT_TOOLS/lib/jetty/jetty-6.1.11-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar" sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/ant-launcher-1.6.5.jar"/>
<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-1.0.jar"/>
diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
index e5e9747..7e11b43 100644
--- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
@@ -119,12 +119,13 @@
if (serializationPolicy == null) {
// Failed to get the requested serialization policy; use the default
- getServletContext().log(
+ log(
"WARNING: Failed to get the SerializationPolicy '"
+ strongName
+ "' for module '"
+ moduleBaseURL
- + "'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.");
+ + "'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.",
+ null);
serializationPolicy = RPC.getDefaultSerializationPolicy();
}
@@ -164,7 +165,7 @@
return RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(),
rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());
} catch (IncompatibleRemoteServiceException ex) {
- getServletContext().log(
+ log(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailure(null, ex);
@@ -197,7 +198,7 @@
modulePath = new URL(moduleBaseURL).getPath();
} catch (MalformedURLException ex) {
// log the information, we will default
- getServletContext().log("Malformed moduleBaseURL: " + moduleBaseURL, ex);
+ log("Malformed moduleBaseURL: " + moduleBaseURL, ex);
}
}
@@ -214,7 +215,7 @@
+ ", is not in the same web application as this servlet, "
+ contextPath
+ ". Your module may not be properly configured or your client and server code maybe out of date.";
- getServletContext().log(message);
+ log(message, null);
} else {
// Strip off the context path from the module base URL. It should be a
// strict prefix.
@@ -232,19 +233,17 @@
serializationPolicy = SerializationPolicyLoader.loadFromStream(is,
null);
} catch (ParseException e) {
- getServletContext().log(
- "ERROR: Failed to parse the policy file '"
- + serializationPolicyFilePath + "'", e);
+ log("ERROR: Failed to parse the policy file '"
+ + serializationPolicyFilePath + "'", e);
} catch (IOException e) {
- getServletContext().log(
- "ERROR: Could not read the policy file '"
- + serializationPolicyFilePath + "'", e);
+ log("ERROR: Could not read the policy file '"
+ + serializationPolicyFilePath + "'", e);
}
} else {
String message = "ERROR: The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
- getServletContext().log(message);
+ log(message, null);
}
} finally {
if (is != null) {
@@ -324,7 +323,7 @@
* Override this method in order to control the parsing of the incoming
* request. For example, you may want to bypass the check of the Content-Type
* and character encoding headers in the request, as some proxies re-write the
- * request headers. Note that bypassing these checks may expose the servlet to
+ * request headers. Note that bypassing these checks may expose the servlet to
* some cross-site vulnerabilities.
*
* @param request the incoming request