]> source.dussan.org Git - vaadin-framework.git/commitdiff
Support for SuperDevMode through ?superdevmode (#8983)
authorArtur Signell <artur@vaadin.com>
Thu, 28 Jun 2012 17:20:00 +0000 (20:20 +0300)
committerArtur Signell <artur@vaadin.com>
Thu, 28 Jun 2012 17:20:00 +0000 (20:20 +0300)
src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java
src/com/vaadin/terminal/gwt/client/SuperDevMode.java [new file with mode: 0644]
src/com/vaadin/terminal/gwt/client/VDebugConsole.java

index 540841a6ae35fbf484c663f1453dabe8499615e9..31775d1d02622096cdd82f1a44555e719cb40cad 100644 (file)
@@ -581,6 +581,11 @@ public class ApplicationConfiguration implements EntryPoint {
             }
         });
 
+        if (SuperDevMode.enableBasedOnParameter()) {
+            // Do not start any application as super dev mode will refresh the
+            // page once done compiling
+            return;
+        }
         registerCallback(GWT.getModuleName());
         deferredWidgetLoader = new DeferredWidgetLoader();
     }
diff --git a/src/com/vaadin/terminal/gwt/client/SuperDevMode.java b/src/com/vaadin/terminal/gwt/client/SuperDevMode.java
new file mode 100644 (file)
index 0000000..77a82c9
--- /dev/null
@@ -0,0 +1,247 @@
+package com.vaadin.terminal.gwt.client;\r
+\r
+import com.google.gwt.core.client.GWT;\r
+import com.google.gwt.core.client.JavaScriptObject;\r
+import com.google.gwt.http.client.UrlBuilder;\r
+import com.google.gwt.jsonp.client.JsonpRequestBuilder;\r
+import com.google.gwt.storage.client.Storage;\r
+import com.google.gwt.user.client.Window.Location;\r
+import com.google.gwt.user.client.rpc.AsyncCallback;\r
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification;\r
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification.EventListener;\r
+import com.vaadin.terminal.gwt.client.ui.notification.VNotification.HideEvent;\r
+\r
+/**\r
+ * Class that enables SuperDevMode using a ?superdevmode parameter in the url.\r
+ * \r
+ * @author Vaadin Ltd\r
+ * @version @VERSION@\r
+ * @since 7.0\r
+ * \r
+ */\r
+public class SuperDevMode {\r
+\r
+    private static final int COMPILE_TIMEOUT_IN_SECONDS = 60;\r
+    protected static final String SKIP_RECOMPILE = "VaadinSuperDevMode_skip_recompile";\r
+\r
+    public static class RecompileResult extends JavaScriptObject {\r
+        protected RecompileResult() {\r
+\r
+        }\r
+\r
+        public final native boolean ok()\r
+        /*-{\r
+         return this.status == "ok";\r
+        }-*/;\r
+    }\r
+\r
+    private static void recompileWidgetsetAndStartInDevMode(\r
+            final String serverUrl) {\r
+        VConsole.log("Recompiling widgetset using<br/>" + serverUrl\r
+                + "<br/>and then reloading in super dev mode");\r
+        VNotification n = new VNotification();\r
+        n.show("<b>Recompiling widgetset, this should not take too long</b>",\r
+                VNotification.CENTERED, VNotification.STYLE_SYSTEM);\r
+\r
+        JsonpRequestBuilder b = new JsonpRequestBuilder();\r
+        b.setCallbackParam("_callback");\r
+        b.setTimeout(COMPILE_TIMEOUT_IN_SECONDS * 1000);\r
+        b.requestObject(serverUrl + "recompile/" + GWT.getModuleName() + "?"\r
+                + getRecompileParameters(GWT.getModuleName()),\r
+                new AsyncCallback<RecompileResult>() {\r
+\r
+                    public void onSuccess(RecompileResult result) {\r
+                        VConsole.log("JSONP compile call successful");\r
+\r
+                        if (!result.ok()) {\r
+                            VConsole.log("* result: " + result);\r
+                            failed();\r
+                            return;\r
+                        }\r
+\r
+                        setSession(\r
+                                getSuperDevModeHookKey(),\r
+                                getSuperDevWidgetSetUrl(GWT.getModuleName(),\r
+                                        serverUrl));\r
+                        setSession(SKIP_RECOMPILE, "1");\r
+\r
+                        VConsole.log("* result: OK. Reloading");\r
+                        Location.reload();\r
+                    }\r
+\r
+                    public void onFailure(Throwable caught) {\r
+                        VConsole.error("JSONP compile call failed");\r
+                        // Don't log exception as they are shown as\r
+                        // notifications\r
+                        VConsole.error(Util.getSimpleName(caught) + ": "\r
+                                + caught.getMessage());\r
+                        failed();\r
+\r
+                    }\r
+\r
+                    private void failed() {\r
+                        VNotification n = new VNotification();\r
+                        n.addEventListener(new EventListener() {\r
+\r
+                            public void notificationHidden(HideEvent event) {\r
+                                recompileWidgetsetAndStartInDevMode(serverUrl);\r
+                            }\r
+                        });\r
+                        n.show("Recompilation failed.<br/>"\r
+                                + "Make sure CodeServer is running, "\r
+                                + "check its output and click to retry",\r
+                                VNotification.CENTERED,\r
+                                VNotification.STYLE_SYSTEM);\r
+                    }\r
+                });\r
+\r
+    }\r
+\r
+    protected static String getSuperDevWidgetSetUrl(String widgetsetName,\r
+            String serverUrl) {\r
+        return serverUrl + GWT.getModuleName() + "/" + GWT.getModuleName()\r
+                + ".nocache.js";\r
+    }\r
+\r
+    private native static String getRecompileParameters(String moduleName)\r
+    /*-{\r
+        var prop_map = $wnd.__gwt_activeModules[moduleName].bindings();\r
+        \r
+        // convert map to URL parameter string\r
+        var props = [];\r
+        for (var key in prop_map) {\r
+           props.push(encodeURIComponent(key) + '=' + encodeURIComponent(prop_map[key]))\r
+        }\r
+        \r
+        return props.join('&') + '&';\r
+    }-*/;\r
+\r
+    private static void setSession(String key, String value) {\r
+        Storage.getSessionStorageIfSupported().setItem(key, value);\r
+    }\r
+\r
+    private static String getSession(String key) {\r
+        return Storage.getSessionStorageIfSupported().getItem(key);\r
+    }\r
+\r
+    private static void removeSession(String key) {\r
+        Storage.getSessionStorageIfSupported().removeItem(key);\r
+    }\r
+\r
+    protected static void disableDevModeAndReload() {\r
+        removeSession(getSuperDevModeHookKey());\r
+        redirect(false);\r
+    }\r
+\r
+    protected static void redirect(boolean devModeOn) {\r
+        UrlBuilder createUrlBuilder = Location.createUrlBuilder();\r
+        if (!devModeOn) {\r
+            createUrlBuilder.removeParameter("superdevmode");\r
+        } else {\r
+            createUrlBuilder.setParameter("superdevmode", "");\r
+        }\r
+\r
+        Location.assign(createUrlBuilder.buildString());\r
+\r
+    }\r
+\r
+    private static String getSuperDevModeHookKey() {\r
+        String widgetsetName = GWT.getModuleName();\r
+        final String superDevModeKey = "__gwtDevModeHook:" + widgetsetName;\r
+        return superDevModeKey;\r
+    }\r
+\r
+    private static boolean hasSession(String key) {\r
+        return getSession(key) != null;\r
+    }\r
+\r
+    /**\r
+     * The URL of the code server. The default URL (http://localhost:9876/) will\r
+     * be used if this is empty or null.\r
+     * \r
+     * @param serverUrl\r
+     *            The url of the code server or null to use the default\r
+     * @return true if recompile started, false if we are running in\r
+     *         SuperDevMode\r
+     */\r
+    protected static boolean recompileIfNeeded(String serverUrl) {\r
+        if (serverUrl == null || "".equals(serverUrl)) {\r
+            serverUrl = "http://localhost:9876/";\r
+        } else {\r
+            serverUrl = "http://" + serverUrl + "/";\r
+        }\r
+\r
+        if (hasSession(SKIP_RECOMPILE)) {\r
+            VConsole.log("Running in SuperDevMode");\r
+            // When we get here, we are running in super dev mode\r
+\r
+            // Remove the flag so next reload will recompile\r
+            removeSession(SKIP_RECOMPILE);\r
+\r
+            // Remove the gwt flag so we will not end up in dev mode if we\r
+            // remove the url parameter manually\r
+            removeSession(getSuperDevModeHookKey());\r
+\r
+            return false;\r
+        }\r
+\r
+        recompileWidgetsetAndStartInDevMode(serverUrl);\r
+        return true;\r
+    }\r
+\r
+    protected static boolean isSuperDevModeEnabledInModule() {\r
+        String moduleName = GWT.getModuleName();\r
+        return isSuperDevModeEnabledInModule(moduleName);\r
+    }\r
+\r
+    protected native static boolean isSuperDevModeEnabledInModule(\r
+            String moduleName)\r
+    /*-{\r
+        if (!$wnd.__gwt_activeModules)\r
+           return false;\r
+        var mod = $wnd.__gwt_activeModules[moduleName];\r
+        if (!mod)\r
+            return false;\r
+\r
+        if (mod.superdevmode) {\r
+           // Running in super dev mode already, it is supported\r
+           return true;\r
+        }\r
+\r
+        return mod.canRedirect;\r
+    }-*/;\r
+\r
+    /**\r
+     * Enables SuperDevMode if the url contains the "superdevmode" parameter.\r
+     * <p>\r
+     * The caller should not continue initialization of the application if this\r
+     * method returns true. The application will be restarted once compilation\r
+     * is done and then this method will return false.\r
+     * </p>\r
+     * \r
+     * @return true if a recompile operation has started and the page will be\r
+     *         reloaded once it is done, false if no recompilation will be done.\r
+     */\r
+    public static boolean enableBasedOnParameter() {\r
+        String superDevModeParameter = Location.getParameter("superdevmode");\r
+        if (superDevModeParameter != null) {\r
+            // Need to check the recompile flag also because if we are running\r
+            // in super dev mode, as a result of the recompile, the enabled\r
+            // check will fail...\r
+            if (!isSuperDevModeEnabledInModule()) {\r
+                showError("SuperDevMode is not enabled for this module/widgetset.<br/>"\r
+                        + "Ensure that your module definition (.gwt.xml) contains <br/>"\r
+                        + "&lt;add-linker name=&quot;xsiframe&quot;/&gt;<br/>"\r
+                        + "&lt;set-configuration-property name=&quot;devModeRedirectEnabled&quot; value=&quot;true&quot; /&gt;<br/>");\r
+                return false;\r
+            }\r
+            return SuperDevMode.recompileIfNeeded(superDevModeParameter);\r
+        }\r
+        return false;\r
+    }\r
+\r
+    private static void showError(String message) {\r
+        VNotification n = new VNotification();\r
+        n.show(message, VNotification.CENTERED_TOP, VNotification.STYLE_SYSTEM);\r
+    }\r
+}\r
index 09e939336e34c632b7c727eea1585c8c787f3784..ce893741d3032bd8fc2c9500b21e8dd575af9407 100644 (file)
@@ -24,6 +24,8 @@ import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.event.dom.client.MouseOutEvent;
 import com.google.gwt.event.dom.client.MouseOutHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.event.shared.UmbrellaException;
 import com.google.gwt.http.client.Request;
@@ -33,6 +35,7 @@ import com.google.gwt.http.client.RequestException;
 import com.google.gwt.http.client.Response;
 import com.google.gwt.http.client.UrlBuilder;
 import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.storage.client.Storage;
 import com.google.gwt.user.client.Cookies;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
@@ -160,7 +163,8 @@ public class VDebugConsole extends VOverlay implements Console {
     private Button savePosition = new Button("S");
     private Button highlight = new Button("H");
     private Button connectorStats = new Button("CS");
-    private CheckBox hostedMode = new CheckBox("GWT");
+    private CheckBox devMode = new CheckBox("Dev");
+    private CheckBox superDevMode = new CheckBox("SDev");
     private CheckBox autoScroll = new CheckBox("Autoscroll ");
     private HorizontalPanel actions;
     private boolean collapsed = false;
@@ -717,33 +721,8 @@ public class VDebugConsole extends VOverlay implements Console {
             savePosition
                     .setTitle("Saves the position and size of debug console to a cookie");
             actions.add(autoScroll);
-            actions.add(hostedMode);
-            if (Location.getParameter("gwt.codesvr") != null) {
-                hostedMode.setValue(true);
-            }
-            hostedMode.addClickHandler(new ClickHandler() {
-                public void onClick(ClickEvent event) {
-                    if (hostedMode.getValue()) {
-                        addHMParameter();
-                    } else {
-                        removeHMParameter();
-                    }
-                }
-
-                private void addHMParameter() {
-                    UrlBuilder createUrlBuilder = Location.createUrlBuilder();
-                    createUrlBuilder.setParameter("gwt.codesvr",
-                            "localhost:9997");
-                    Location.assign(createUrlBuilder.buildString());
-                }
-
-                private void removeHMParameter() {
-                    UrlBuilder createUrlBuilder = Location.createUrlBuilder();
-                    createUrlBuilder.removeParameter("gwt.codesvr");
-                    Location.assign(createUrlBuilder.buildString());
-
-                }
-            });
+            addDevMode();
+            addSuperDevMode();
 
             autoScroll
                     .setTitle("Automatically scroll so that new messages are visible");
@@ -861,6 +840,54 @@ public class VDebugConsole extends VOverlay implements Console {
 
     }
 
+    private void addSuperDevMode() {
+        final Storage sessionStorage = Storage.getSessionStorageIfSupported();
+        if (sessionStorage == null) {
+            return;
+        }
+        actions.add(superDevMode);
+        if (Location.getParameter("superdevmode") != null) {
+            superDevMode.setValue(true);
+        }
+        superDevMode.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
+
+            public void onValueChange(ValueChangeEvent<Boolean> event) {
+                SuperDevMode.redirect(event.getValue());
+            }
+
+        });
+
+    }
+
+    private void addDevMode() {
+        actions.add(devMode);
+        if (Location.getParameter("gwt.codesvr") != null) {
+            devMode.setValue(true);
+        }
+        devMode.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                if (devMode.getValue()) {
+                    addHMParameter();
+                } else {
+                    removeHMParameter();
+                }
+            }
+
+            private void addHMParameter() {
+                UrlBuilder createUrlBuilder = Location.createUrlBuilder();
+                createUrlBuilder.setParameter("gwt.codesvr", "localhost:9997");
+                Location.assign(createUrlBuilder.buildString());
+            }
+
+            private void removeHMParameter() {
+                UrlBuilder createUrlBuilder = Location.createUrlBuilder();
+                createUrlBuilder.removeParameter("gwt.codesvr");
+                Location.assign(createUrlBuilder.buildString());
+
+            }
+        });
+    }
+
     protected void dumpConnectorInfo(ApplicationConnection a) {
         RootConnector root = a.getRootConnector();
         log("================");