Super Dev Mode: replace public event logging API
- RecompileListener is deprecated.
- JobChangeListener is the new public API.
The new listener takes a JobEvent, which is designed
so we can add new public properties easily. JobEvent
replaces the old Progress class. I added property
validation and one more status for a failed compile.
Command line interface changes:
- Check earlier if a supplied module name is invalid.
HTTP API changes:
- For /progress, I renamed "stepMessage" field to "message",
and removed finishedSteps and totalSteps.
I removed the step information since it's unused and
will be tricky to calculate. We can add it back later
if needed, but compiles normally won't take that long.
ModuleDefSchema: extracted a method to check if a property
name is valid.
Change-Id: Ic69984c7090ca6458f3e467d6b431cc1e4904000
Review-Link: https://gwt-review.googlesource.com/#/c/9084/
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
index c23fecd..4ba3b9a 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/CodeServer.java
@@ -118,14 +118,14 @@
TreeLogger startupLogger = topLogger.branch(Type.INFO, "Super Dev Mode starting up");
OutboxTable outboxes = makeOutboxes(options, startupLogger);
- ProgressTable progressTable = new ProgressTable();
- JobRunner runner = new JobRunner(progressTable, outboxes);
+ JobEventTable eventTable = new JobEventTable();
+ JobRunner runner = new JobRunner(eventTable, outboxes);
JsonExporter exporter = new JsonExporter(options, outboxes);
SourceHandler sourceHandler = new SourceHandler(outboxes, exporter);
WebServer webServer = new WebServer(sourceHandler, exporter, outboxes,
- runner, progressTable, options.getBindAddress(), options.getPort());
+ runner, eventTable, options.getBindAddress(), options.getPort());
webServer.start(topLogger);
return webServer;
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
index 586d3ca..845215e 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Job.java
@@ -17,7 +17,9 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
-import com.google.gwt.dev.codeserver.Progress.Status;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.codeserver.JobEvent.Status;
+import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableSortedMap;
import com.google.gwt.thirdparty.guava.common.util.concurrent.Futures;
import com.google.gwt.thirdparty.guava.common.util.concurrent.ListenableFuture;
@@ -32,7 +34,7 @@
* A request for Super Dev Mode to compile something.
*
* <p>Each job has a lifecycle where it goes through up to four states. See
- * {@link Progress.Status}.
+ * {@link JobEvent.Status}.
*
* <p>Jobs are thread-safe.
*/
@@ -58,28 +60,21 @@
private final Outbox outbox;
private final RecompileListener recompileListener;
+ private final JobChangeListener jobChangeListener;
private final LogSupplier logSupplier;
- private ProgressTable table; // non-null when submitted
+ private JobEventTable table; // non-null when submitted
- // Progress
-
- /**
- * The number of calls to {@link #onCompilerProgress}.
- */
- private int finishedSteps = 0;
-
- /**
- * The estimated total number of calls to {@link #onCompilerProgress}.
- */
- private int totalSteps = -1; // non-negative after the compile has started
+ // Miscellaneous
/**
* The id to report to the recompile listener.
*/
private int compileId = -1; // non-negative after the compile has started
- private Exception recompileListenerFailure;
+ private CompileDir compileDir; // non-null after the compile has started
+
+ private Exception listenerFailure;
/**
* Creates a job to update an outbox.
@@ -88,21 +83,27 @@
* @param parentLogger The parent of the logger that will be used for this job.
*/
Job(Outbox box, Map<String, String> bindingProperties,
- TreeLogger parentLogger, RecompileListener recompileListener) {
+ TreeLogger parentLogger, RecompileListener recompileListener,
+ JobChangeListener jobChangeListener) {
this.id = chooseNextId(box);
this.outbox = box;
this.inputModuleName = box.getInputModuleName();
// TODO: we will use the binding properties to find or create the outbox,
// then take binding properties from the outbox here.
this.bindingProperties = ImmutableSortedMap.copyOf(bindingProperties);
- this.recompileListener = recompileListener;
+ this.recompileListener = Preconditions.checkNotNull(recompileListener);
+ this.jobChangeListener = Preconditions.checkNotNull(jobChangeListener);
this.logSupplier = new LogSupplier(parentLogger, id);
}
+ static boolean isValidJobId(String id) {
+ return ModuleDef.isValidModuleName(id);
+ }
+
private static String chooseNextId(Outbox box) {
String prefix = box.getId();
prefixToNextId.putIfAbsent(prefix, new AtomicInteger(0));
- return prefix + "-" + prefixToNextId.get(prefix).getAndIncrement();
+ return prefix + "_" + prefixToNextId.get(prefix).getAndIncrement();
}
/**
@@ -157,8 +158,8 @@
return result;
}
- Exception getRecompileListenerFailure() {
- return recompileListenerFailure;
+ Exception getListenerFailure() {
+ return listenerFailure;
}
// === state transitions ===
@@ -180,49 +181,43 @@
* Starts sending updates to the JobTable.
* @throws IllegalStateException if the job was already started.
*/
- synchronized void onSubmitted(ProgressTable table) {
+ synchronized void onSubmitted(JobEventTable table) {
if (wasSubmitted()) {
throw new IllegalStateException("compile job has already started: " + id);
}
this.table = table;
- table.publish(new Progress(this, Status.WAITING), getLogger());
+ table.publish(makeEvent(Status.WAITING), getLogger());
}
/**
* Reports that we started to compile the job.
*/
- synchronized void onStarted(int totalSteps, int compileId, CompileDir compileDir) {
- if (totalSteps < 0) {
- throw new IllegalArgumentException("totalSteps should not be negative: " + totalSteps);
- }
+ synchronized void onStarted(int compileId, CompileDir compileDir) {
if (table == null || !table.isActive(this)) {
throw new IllegalStateException("compile job is not active: " + id);
}
- if (this.totalSteps >= 0) {
- throw new IllegalStateException("onStarted already called for " + id);
- }
- this.totalSteps = totalSteps;
this.compileId = compileId;
+ this.compileDir = compileDir;
try {
recompileListener.startedCompile(inputModuleName, compileId, compileDir);
} catch (Exception e) {
getLogger().log(TreeLogger.Type.WARN, "recompile listener threw exception", e);
- recompileListenerFailure = e;
+ listenerFailure = e;
}
+
+ publish(makeEvent(Status.COMPILING));
}
/**
- * Reports that this job has made progress.
+ * Reports that this job has made progress while compiling.
* @throws IllegalStateException if the job is not running.
*/
- synchronized void onCompilerProgress(String stepMessage) {
- if (table == null || !table.isActive(this)) {
- throw new IllegalStateException("compile job is not active: " + id);
+ synchronized void onProgress(String stepMessage) {
+ if (table == null || table.getPublishedEvent(this).getStatus() != Status.COMPILING) {
+ throw new IllegalStateException("onProgress called for a job that isn't compiling: " + id);
}
- finishedSteps++;
- table.publish(new Progress.Compiling(this, finishedSteps, totalSteps, stepMessage),
- getLogger());
+ publish(makeEvent(Status.COMPILING, stepMessage));
}
/**
@@ -235,20 +230,20 @@
}
// Report that we finished unless the listener messed up already.
- if (recompileListenerFailure == null) {
+ if (listenerFailure == null) {
try {
recompileListener.finishedCompile(inputModuleName, compileId, newResult.isOk());
} catch (Exception e) {
getLogger().log(TreeLogger.Type.WARN, "recompile listener threw exception", e);
- recompileListenerFailure = e;
+ listenerFailure = e;
}
}
result.set(newResult);
if (newResult.isOk()) {
- table.publish(new Progress(this, Status.SERVING), getLogger());
+ publish(makeEvent(Status.SERVING));
} else {
- table.publish(new Progress(this, Status.GONE), getLogger());
+ publish(makeEvent(Status.ERROR));
}
}
@@ -259,7 +254,37 @@
if (table == null || !table.isActive(this)) {
throw new IllegalStateException("compile job is not active: " + id);
}
- table.publish(new Progress(this, Status.GONE), getLogger());
+ publish(makeEvent(Status.GONE));
+ }
+
+ private JobEvent makeEvent(Status status) {
+ return makeEvent(status, null);
+ }
+
+ private JobEvent makeEvent(Status status, String message) {
+ JobEvent.Builder out = new JobEvent.Builder();
+ out.setJobId(getId());
+ out.setInputModuleName(getInputModuleName());
+ out.setBindings(getBindingProperties());
+ out.setStatus(status);
+ out.setMessage(message);
+ out.setCompileDir(compileDir);
+ return out.build();
+ }
+
+ /**
+ * Makes an event visible externally.
+ */
+ private void publish(JobEvent event) {
+ if (listenerFailure == null) {
+ try {
+ jobChangeListener.onJobChange(event);
+ } catch (Exception e) {
+ getLogger().log(Type.WARN, "JobChangeListener threw exception", e);
+ listenerFailure = e;
+ }
+ }
+ table.publish(event, getLogger());
}
/**
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobChangeListener.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobChangeListener.java
new file mode 100644
index 0000000..7ad8f03
--- /dev/null
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobChangeListener.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 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.codeserver;
+
+/**
+ * A callback for receiving events when a GWT compile job changes status.
+ *
+ * This interface replaces {@link RecompileListener}.
+ */
+public interface JobChangeListener {
+ void onJobChange(JobEvent event);
+
+ /**
+ * A listener that does nothing.
+ */
+ static final JobChangeListener NONE = new JobChangeListener() {
+ @Override
+ public void onJobChange(JobEvent event) {
+ }
+ };
+}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java
new file mode 100644
index 0000000..915b248
--- /dev/null
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEvent.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2014 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.codeserver;
+
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefSchema;
+import com.google.gwt.thirdparty.guava.common.base.Preconditions;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableSortedMap;
+
+import java.util.Map;
+import java.util.SortedMap;
+
+/**
+ * The status of a compile job submitted to Super Dev Mode.
+ *
+ * <p>JobEvent objects are deeply immutable, though they describe a Job that changes.
+ */
+public final class JobEvent {
+
+ private final String jobId;
+
+ private final String inputModuleName;
+ private final ImmutableSortedMap<String, String> bindings;
+ private final Status status;
+ private final String message;
+
+ private final CompileDir compileDir;
+
+ private JobEvent(Builder builder) {
+ this.jobId = Preconditions.checkNotNull(builder.jobId);
+ this.inputModuleName = Preconditions.checkNotNull(builder.inputModuleName);
+ this.bindings = ImmutableSortedMap.copyOf(builder.bindings);
+ this.status = Preconditions.checkNotNull(builder.status);
+ this.message = builder.message == null ? status.defaultMessage : builder.message;
+
+ // The following fields may be null.
+ this.compileDir = builder.compileDir;
+
+ // Any new fields added should allow nulls for backward compatibility.
+ }
+
+ /**
+ * The id of the job being compiled. Unique within the same CodeServer process.
+ * This should be considered an opaque string.
+ */
+ public String getJobId() {
+ return jobId;
+ }
+
+ /**
+ * The module name sent to the GWT compiler to start the compile.
+ */
+ public String getInputModuleName() {
+ return inputModuleName;
+ }
+
+ /**
+ * The binding properties sent to the GWT compiler.
+ */
+ public SortedMap<String, String> getBindings() {
+ // Can't return ImmutableSortedMap here because it's repackaged and this is a public API.
+ return bindings;
+ }
+
+ /**
+ * The last reported status of the job.
+ */
+ public Status getStatus() {
+ return status;
+ }
+
+ /**
+ * Returns a line of text describing the job's current status.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Returns the directory where the GWT module is being compiled, or null if not available.
+ * (Not available for jobs that are WAITING.)
+ */
+ public CompileDir getCompileDir() {
+ return compileDir;
+ }
+
+ /**
+ * Defines the lifecycle of a job.
+ */
+ public static enum Status {
+ WAITING("waiting", "Waiting for the compiler to start"),
+ COMPILING("compiling", "Compiling"),
+ SERVING("serving", "Compiled output is ready"),
+ GONE("gone", "Compiled output is no longer available"),
+ ERROR("error", "Compile failed with an error");
+
+ final String jsonName;
+ final String defaultMessage;
+
+ Status(String jsonName, String defaultMessage) {
+ this.jsonName = jsonName;
+ this.defaultMessage = defaultMessage;
+ }
+ }
+
+ /**
+ * Creates a JobEvent.
+ * This is public to allow external tests of code that implements {@link JobChangeListener}.
+ * Normally all JobEvents are created in the code server.
+ */
+ public static class Builder {
+ private String jobId;
+
+ private String inputModuleName;
+ private Map<String, String> bindings = ImmutableMap.of();
+ private Status status;
+ private String message;
+ private CompileDir compileDir;
+
+ /**
+ * A unique id for this job. Required.
+ */
+ public void setJobId(String jobId) {
+ Preconditions.checkArgument(Job.isValidJobId(jobId), "invalid job id: " + jobId);
+ this.jobId = jobId;
+ }
+
+ /**
+ * The name of the module as passed to the compiler. Required.
+ */
+ public void setInputModuleName(String inputModuleName) {
+ Preconditions.checkArgument(ModuleDef.isValidModuleName(inputModuleName),
+ "invalid module name: " + jobId);
+ this.inputModuleName = inputModuleName;
+ }
+
+ /**
+ * The bindings passed to the compiler.
+ * Optional, but may not be null. (Defaults to the empty map.)
+ */
+ public void setBindings(Map<String, String> bindings) {
+ for (String name : bindings.keySet()) {
+ if (!ModuleDefSchema.isValidPropertyName(name)) {
+ throw new IllegalArgumentException("invalid property name: " + name);
+ }
+ }
+ this.bindings = bindings;
+ }
+
+ /**
+ * The job's current status. Required.
+ */
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ /**
+ * A message to describing the job's current state.
+ * It should be a single line of text.
+ * Optional. If null, a default message will be used.
+ */
+ public void setMessage(String message) {
+ if (message != null) {
+ Preconditions.checkArgument(!message.contains("\n"),
+ "JobEvent messages should be a single line of text");
+ }
+ this.message = message;
+ }
+
+ /**
+ * The directory where the GWT compiler will write its output.
+ * Optional. (Not available until the compile starts.)
+ */
+ public void setCompileDir(CompileDir compileDir) {
+ this.compileDir = compileDir;
+ }
+
+ public JobEvent build() {
+ return new JobEvent(this);
+ }
+ }
+}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/ProgressTable.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEventTable.java
similarity index 60%
rename from dev/codeserver/java/com/google/gwt/dev/codeserver/ProgressTable.java
rename to dev/codeserver/java/com/google/gwt/dev/codeserver/JobEventTable.java
index 4711908..83bf47b 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/ProgressTable.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobEventTable.java
@@ -17,7 +17,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
-import com.google.gwt.dev.codeserver.Progress.Status;
+import com.google.gwt.dev.codeserver.JobEvent.Status;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
@@ -26,14 +26,15 @@
import java.util.Set;
/**
- * Contains the progress of all the jobs that have been submitted to the JobRunner.
+ * Contains the current status of each {@link Job}.
+ * (That is, the most recently reported event.)
*/
-class ProgressTable {
+class JobEventTable {
/**
- * The progress of each known job, by job id.
+ * The most recent event sent by each job.
*/
- private final Map<String, Progress> progressById = Maps.newHashMap();
+ private final Map<String, JobEvent> eventsByJobId = Maps.newHashMap();
/**
* A set of submitted job ids that are still active, in the order they were submitted.
@@ -48,36 +49,47 @@
private final Set<String> compilingJobIds = new LinkedHashSet<String>();
/**
+ * Returns the event that's currently published for the given job.
+ */
+ synchronized JobEvent getPublishedEvent(Job job) {
+ return eventsByJobId.get(job.getId());
+ }
+
+ /**
* Publishes the progress of a job after it changed. (This replaces any previous progress.)
*/
- synchronized void publish(Progress progress, TreeLogger logger) {
- String id = progress.jobId;
+ synchronized void publish(JobEvent event, TreeLogger logger) {
+ String id = event.getJobId();
- progressById.put(id, progress);
+ eventsByJobId.put(id, event);
// Update indexes
- if (progress.isActive()) {
+ if (isActive(event.getStatus())) {
activeJobIds.add(id);
} else {
activeJobIds.remove(id);
}
- if (progress.status == Status.COMPILING) {
+ if (event.getStatus() == Status.COMPILING) {
compilingJobIds.add(id);
assert compilingJobIds.size() <= 1;
} else {
compilingJobIds.remove(id);
}
- logger.log(Type.TRACE, "job's progress set to " + progress.status + ": " + id);
+ logger.log(Type.TRACE, "job's progress set to " + event.getStatus() + ": " + id);
+ }
+
+ private static boolean isActive(Status status) {
+ return status == Status.WAITING || status == Status.COMPILING || status == Status.SERVING;
}
/**
* Returns true if the job's status was ever published.
*/
synchronized boolean wasSubmitted(Job job) {
- return progressById.containsKey(job.getId());
+ return eventsByJobId.containsKey(job.getId());
}
synchronized boolean isActive(Job job) {
@@ -85,27 +97,29 @@
}
/**
- * Returns the progress of the job that's currently being compiled, or null if idle.
+ * Returns an event indicating the current status of the job that's currently being compiled,
+ * or null if idle.
*/
- synchronized Progress getProgressForCompilingJob() {
+ synchronized JobEvent getCompilingJobEvent() {
if (compilingJobIds.isEmpty()) {
return null;
}
String id = compilingJobIds.iterator().next();
- Progress progress = progressById.get(id);
- assert progress != null;
- return progress;
+ JobEvent event = eventsByJobId.get(id);
+ assert event != null;
+ return event;
}
/**
- * Returns the progress of all active jobs, in the order submitted.
+ * Returns an event indicating the current status of each active job, in the order they were
+ * submitted.
* TODO: hook this up.
*/
- synchronized ImmutableList<Progress> getProgressForActiveJobs() {
- ImmutableList.Builder<Progress> builder = ImmutableList.builder();
+ synchronized ImmutableList<JobEvent> getActiveEvents() {
+ ImmutableList.Builder<JobEvent> builder = ImmutableList.builder();
for (String id : activeJobIds) {
- Progress p = progressById.get(id);
+ JobEvent p = eventsByJobId.get(id);
assert p != null;
builder.add(p);
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobRunner.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobRunner.java
index 923b528..5ae4694 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/JobRunner.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JobRunner.java
@@ -29,11 +29,11 @@
* <p>JobRunners are thread-safe.
*/
public class JobRunner {
- private final ProgressTable table;
+ private final JobEventTable table;
private final OutboxTable outboxes;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
- JobRunner(ProgressTable table, OutboxTable outboxes) {
+ JobRunner(JobEventTable table, OutboxTable outboxes) {
this.table = table;
this.outboxes = outboxes;
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/JsonExporter.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/JsonExporter.java
index a7cb781..5f645c8 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/JsonExporter.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/JsonExporter.java
@@ -54,19 +54,14 @@
/**
* Creates the response to a /progress request.
*/
- JsonObject exportProgressResponse(Progress progress) {
+ JsonObject exportProgressResponse(JobEvent progress) {
// TODO: upgrade for multiple compiles and finalize API for 2.7.
JsonObject out = new JsonObject();
- out.put("jobId", progress.jobId);
- out.put("status", progress.status.jsonName);
- out.put("inputModule", progress.inputModuleName);
- out.put("bindings", exportMap(progress.bindings));
- if (progress instanceof Progress.Compiling) {
- Progress.Compiling compiling = (Progress.Compiling) progress;
- out.put("finishedSteps", compiling.finishedSteps);
- out.put("totalSteps", compiling.totalSteps);
- out.put("stepMessage", compiling.stepMessage);
- }
+ out.put("jobId", progress.getJobId());
+ out.put("status", progress.getStatus().jsonName);
+ out.put("message", progress.getMessage());
+ out.put("inputModule", progress.getInputModuleName());
+ out.put("bindings", exportMap(progress.getBindings()));
return out;
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
index 7a1c789..3d4f6c5 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Options.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.dev.ArgProcessorBase;
+import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.util.arg.ArgHandlerJsInteropMode;
import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
import com.google.gwt.dev.util.arg.ArgHandlerSourceLevel;
@@ -56,7 +57,10 @@
private String bindAddress = "127.0.0.1";
private String preferredHost = "localhost";
private int port = 9876;
+
private RecompileListener recompileListener = RecompileListener.NONE;
+ private JobChangeListener jobChangeListener = JobChangeListener.NONE;
+
private TreeLogger.Type logLevel = TreeLogger.Type.INFO;
// Use the same default as the GWT compiler.
private SourceLevel sourceLevel = SourceLevel.DEFAULT_SOURCE_LEVEL;
@@ -102,7 +106,10 @@
/**
* A Java application that embeds Super Dev Mode can use this hook to find out
* when compiles start and end.
+ *
+ * @deprecated replaced by {@link #setJobChangeListener}
*/
+ @Deprecated
public void setRecompileListener(RecompileListener recompileListener) {
this.recompileListener = recompileListener;
}
@@ -112,6 +119,20 @@
}
/**
+ * A Java application that embeds Super Dev Mode can use this hook to find out
+ * when compile jobs change state.
+ *
+ * <p>Replaces {@link #setRecompileListener}
+ */
+ public void setJobChangeListener(JobChangeListener jobChangeListener) {
+ this.jobChangeListener = jobChangeListener;
+ }
+
+ JobChangeListener getJobChangeListener() {
+ return jobChangeListener;
+ }
+
+ /**
* The top level of the directory tree where the code server keeps compiler output.
*/
File getWorkDir() {
@@ -626,6 +647,10 @@
@Override
public boolean addExtraArg(String arg) {
+ if (!ModuleDef.isValidModuleName(arg)) {
+ System.err.println("Invalid module name: '" + arg + "'");
+ return false;
+ }
moduleNames.add(arg);
return true;
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
index f2275f2..be501e6 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Outbox.java
@@ -18,7 +18,9 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.codeserver.Job.Result;
+import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import java.io.BufferedInputStream;
import java.io.File;
@@ -51,12 +53,17 @@
Outbox(String id, Recompiler recompiler, Options options, TreeLogger logger)
throws UnableToCompleteException {
+ Preconditions.checkArgument(isValidOutboxId(id));
this.id = id;
this.recompiler = recompiler;
this.options = options;
maybePrecompile(logger);
}
+ private boolean isValidOutboxId(String id) {
+ return ModuleDef.isValidModuleName(id);
+ }
+
/**
* A unique id for this outbox. (This should be treated as an opaque string.)
*/
@@ -82,14 +89,16 @@
// Create a dummy job for the first compile.
// Its progress is not visible externally but will still be logged.
- ProgressTable dummy = new ProgressTable();
+ JobEventTable dummy = new JobEventTable();
Job job = makeJob(defaultProps, logger);
job.onSubmitted(dummy);
publish(recompiler.precompile(job), job);
if (options.isCompileTest()) {
+
// Listener errors are fatal in compile tests
- Throwable error = job.getRecompileListenerFailure();
+
+ Throwable error = job.getListenerFailure();
if (error != null) {
UnableToCompleteException e = new UnableToCompleteException();
e.initCause(error);
@@ -102,7 +111,8 @@
* Creates a Job whose output will be saved in this outbox.
*/
Job makeJob(Map<String, String> bindingProperties, TreeLogger parentLogger) {
- return new Job(this, bindingProperties, parentLogger, options.getRecompileListener());
+ return new Job(this, bindingProperties, parentLogger,
+ options.getRecompileListener(), options.getJobChangeListener());
}
/**
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Progress.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Progress.java
deleted file mode 100644
index b84c73f..0000000
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Progress.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2014 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.codeserver;
-
-import com.google.gwt.thirdparty.guava.common.collect.ImmutableSortedMap;
-
-/**
- * A snapshot of a {@link Job}'s current state, for progress dialogs.
- */
-class Progress {
-
- /**
- * The id of the job being compiled. (Unique within the same CodeServer process.)
- */
- final String jobId;
-
- final String inputModuleName;
- final ImmutableSortedMap<String, String> bindings;
- final Status status;
-
- Progress(Job job, Status status) {
- this.jobId = job.getId();
- this.inputModuleName = job.getInputModuleName();
- this.bindings = job.getBindingProperties();
- this.status = status;
- }
-
- /**
- * Returns true if the job's progress should be shown in the progress view.
- * (For jobs that are GONE, their status is only available by request.)
- */
- public boolean isActive() {
- return status == Status.WAITING || status == Status.COMPILING || status == Status.SERVING;
- }
-
- /**
- * Defines the lifecycle of a job.
- */
- static enum Status {
- WAITING("waiting"),
- COMPILING("compiling"),
- SERVING("serving"), // Output is available to HTTP requests
- GONE("gone"); // Output directory is no longer being served
-
- final String jsonName;
-
- Status(String jsonName) {
- this.jsonName = jsonName;
- }
- }
-
- /**
- * Returned when a compile is in progress.
- */
- static class Compiling extends Progress {
-
- /**
- * The number of steps finished, for showing progress.
- */
- final int finishedSteps;
-
- /**
- * The number of steps total, for showing progress.
- */
- final int totalSteps;
-
- /**
- * A message about the current step being executed.
- */
- final String stepMessage;
-
- Compiling(Job job, int finishedSteps, int totalSteps, String stepMessage) {
- super(job, Status.COMPILING);
- this.finishedSteps = finishedSteps;
- this.totalSteps = totalSteps;
- this.stepMessage = stepMessage;
- }
- }
-}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/RecompileListener.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/RecompileListener.java
index b8b688a..e0670fc 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/RecompileListener.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/RecompileListener.java
@@ -18,7 +18,10 @@
/**
* A callback interface that can be used to find out when Super Dev Mode starts and
* finishes its compiles.
+ *
+ * @deprecated replaced by {@link JobChangeListener}.
*/
+@Deprecated
public interface RecompileListener {
/**
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
index d2ed011..789fb81 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
@@ -153,12 +153,11 @@
CompileDir compileDir = makeCompileDir(compileId, job.getLogger());
TreeLogger compileLogger = makeCompileLogger(compileDir, job.getLogger());
- int totalSteps = options.shouldCompileIncremental() ? 1 : 2;
- job.onStarted(totalSteps, compileId, compileDir);
+ job.onStarted(compileId, compileDir);
boolean success;
if (options.shouldCompileIncremental()) {
- job.onCompilerProgress("Compiling (incrementally)");
+ job.onProgress("Compiling (incrementally)");
success = compileIncremental(compileLogger, compileDir);
} else {
success = compileMonolithic(compileLogger, compileDir, job);
@@ -261,7 +260,7 @@
private boolean compileMonolithic(TreeLogger compileLogger, CompileDir compileDir, Job job)
throws UnableToCompleteException {
- job.onCompilerProgress("Loading modules");
+ job.onProgress("Loading modules");
CompilerOptions loadOptions = new CompilerOptionsImpl(compileDir, inputModuleName, options);
compilerContext = compilerContextBuilder.options(loadOptions).build();
@@ -281,7 +280,7 @@
return true;
}
- job.onCompilerProgress("Compiling");
+ job.onProgress("Compiling");
// TODO: use speed tracer to get more compiler events?
CompilerOptions runOptions = new CompilerOptionsImpl(compileDir, newModuleName, options);
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
index bcede76..d5a1e74 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
@@ -93,7 +93,7 @@
private final JsonExporter jsonExporter;
private final OutboxTable outboxes;
private final JobRunner runner;
- private final ProgressTable progressTable;
+ private final JobEventTable eventTable;
private final String bindAddress;
private final int port;
@@ -101,12 +101,12 @@
private Server server;
WebServer(SourceHandler handler, JsonExporter jsonExporter, OutboxTable outboxes,
- JobRunner runner, ProgressTable progressTable, String bindAddress, int port) {
+ JobRunner runner, JobEventTable eventTable, String bindAddress, int port) {
this.handler = handler;
this.jsonExporter = jsonExporter;
this.outboxes = outboxes;
this.runner = runner;
- this.progressTable = progressTable;
+ this.eventTable = eventTable;
this.bindAddress = bindAddress;
this.port = port;
}
@@ -249,14 +249,14 @@
if (target.equals("/progress")) {
setHandled(request);
// TODO: return a list of progress objects here, one for each job.
- Progress progress = progressTable.getProgressForCompilingJob();
+ JobEvent event = eventTable.getCompilingJobEvent();
JsonObject json;
- if (progress == null) {
+ if (event == null) {
json = new JsonObject();
json.put("status", "idle");
} else {
- json = jsonExporter.exportProgressResponse(progress);
+ json = jsonExporter.exportProgressResponse(event);
}
sendJsonResult(json, request, response, logger);
return;
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 27beb1d..94b900e 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -1192,15 +1192,10 @@
@Override
public Object convertToArg(Schema schema, int line, String elem,
String attr, String value) throws UnableToCompleteException {
- // Ensure each part of the name is valid.
- //
- String[] tokens = (value + ". ").split("\\.");
- for (int i = 0; i < tokens.length - 1; i++) {
- String token = tokens[i];
- if (!Util.isValidJavaIdent(token)) {
- Messages.PROPERTY_NAME_INVALID.log(logger, line, value, null);
- throw new UnableToCompleteException();
- }
+
+ if (!isValidPropertyName(value)) {
+ Messages.PROPERTY_NAME_INVALID.log(logger, line, value, null);
+ throw new UnableToCompleteException();
}
// It is a valid name.
@@ -1209,6 +1204,20 @@
}
}
+ public static boolean isValidPropertyName(String value) {
+ boolean isValid = true;
+ // Ensure each part of the name is valid.
+ //
+ String[] tokens = (value + ". ").split("\\.");
+ for (int i = 0; i < tokens.length - 1; i++) {
+ String token = tokens[i];
+ if (!Util.isValidJavaIdent(token)) {
+ isValid = false;
+ }
+ }
+ return isValid;
+ }
+
private static class PropertyValue {
public final String token;