Last few changes necessary to make first embeddable graphs work
* add EmbeddedTkoClient GWT entry point + associated scripts, GWT XML, launch configuration, apache config
* added EmbeddedTkoClientTest.html, a simple demonstration/test of using embedded widgets
* modify TabView to no longer be a Composite, but instead have a getWidget element.  this allows us to defer any DOM manipulation to initialize() and therefore avoid executing it at all in the embedded case.  the introduction of code to TabView.initialize() (it was previous abstract) required adding a super.initalize() call to *all* subclasses, and there are a lot, hence the large number of files in this change.
* added Plot.getNativeProxy(), generating a native JS object that acts as a proxy to the GWT Plot object
* extend JsonRpcProxy to allow use of PaddedJsonRpcProxys
* remove debug prints from PaddedJsonRpcProxy
* fix a little bug where a return statement was missing from Plot.showDrilldown()

Signed-off-by: Steve Howard <[email protected]>


git-svn-id: http://test.kernel.org/svn/autotest/trunk@3039 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/frontend/client/EmbeddedTkoClient-compile b/frontend/client/EmbeddedTkoClient-compile
new file mode 100644
index 0000000..cbfb6ea
--- /dev/null
+++ b/frontend/client/EmbeddedTkoClient-compile
@@ -0,0 +1,8 @@
+#!/bin/sh
+APPDIR=`dirname $0`;
+GWTDIR=`$APPDIR/gwt_dir`;
+java  -Xmx512M \
+  -cp "$APPDIR/src:$APPDIR/bin:$GWTDIR/gwt-user.jar:$GWTDIR/gwt-dev-linux.jar" \
+  -Djava.awt.headless=true \
+  com.google.gwt.dev.GWTCompiler -out "$APPDIR/www" "$@" \
+  autotest.EmbeddedTkoClient
diff --git a/frontend/client/EmbeddedTkoClient-shell b/frontend/client/EmbeddedTkoClient-shell
new file mode 100644
index 0000000..b58ea24
--- /dev/null
+++ b/frontend/client/EmbeddedTkoClient-shell
@@ -0,0 +1,4 @@
+#!/bin/sh
+APPDIR=`dirname $0`;
+GWTDIR=`$APPDIR/gwt_dir`;
+java  -cp "$APPDIR/src:$APPDIR/bin:$GWTDIR/gwt-user.jar:$GWTDIR/gwt-dev-linux.jar" com.google.gwt.dev.GWTShell -out "$APPDIR/www" "$@" http://localhost:8000/new_tko/server/autotest.EmbeddedTkoClient/EmbeddedTkoClientTest.html;
diff --git a/frontend/client/EmbeddedTkoClient.launch b/frontend/client/EmbeddedTkoClient.launch
new file mode 100644
index 0000000..028abc6
--- /dev/null
+++ b/frontend/client/EmbeddedTkoClient.launch
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/AfeClient"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;AfeClient&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/AfeClient/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento project=&quot;AfeClient&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;/usr/local/lib/gwt/gwt-dev-linux.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.GWTShell"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-out www http://localhost:8000/new_tko/server/autotest.EmbeddedTkoClient/EmbeddedTkoClientTest.html"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="AfeClient"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx512M"/>
+</launchConfiguration>
diff --git a/frontend/client/src/autotest/EmbeddedTkoClient.gwt.xml b/frontend/client/src/autotest/EmbeddedTkoClient.gwt.xml
new file mode 100644
index 0000000..20eb6dd
--- /dev/null
+++ b/frontend/client/src/autotest/EmbeddedTkoClient.gwt.xml
@@ -0,0 +1,16 @@
+<module>
+  <inherits name='com.google.gwt.user.User'/>
+  <inherits name='com.google.gwt.json.JSON'/>
+  <inherits name='com.google.gwt.http.HTTP'/>
+
+  <source path="tko"/>
+  <source path="common"/>
+  <entry-point class='autotest.tko.EmbeddedTkoClient'/>
+  
+  <stylesheet src='common.css'/>
+  <stylesheet src='standard.css'/>
+  <stylesheet src='afeclient.css'/>
+  <stylesheet src='tkoclient.css'/>
+  
+  <!-- <set-property name="user.agent" value="gecko"/> -->
+</module>
diff --git a/frontend/client/src/autotest/afe/CreateJobView.java b/frontend/client/src/autotest/afe/CreateJobView.java
index 5b8da69..cb68457 100644
--- a/frontend/client/src/autotest/afe/CreateJobView.java
+++ b/frontend/client/src/autotest/afe/CreateJobView.java
@@ -396,6 +396,7 @@
     
     @Override
     public void initialize() {
+        super.initialize();
         populatePriorities(staticData.getData("priorities").isArray());
         
         kernel.addFocusListener(new FocusListener() {
diff --git a/frontend/client/src/autotest/afe/HostListView.java b/frontend/client/src/autotest/afe/HostListView.java
index 8c9d258..050613b 100644
--- a/frontend/client/src/autotest/afe/HostListView.java
+++ b/frontend/client/src/autotest/afe/HostListView.java
@@ -30,6 +30,7 @@
     
     @Override
     public void initialize() {
+        super.initialize();
         table.setClickable(true);
         table.addListener(new DynamicTableListener() {
             public void onRowClicked(int rowIndex, JSONObject row) {
diff --git a/frontend/client/src/autotest/afe/JobListView.java b/frontend/client/src/autotest/afe/JobListView.java
index e62c509..7741afa 100644
--- a/frontend/client/src/autotest/afe/JobListView.java
+++ b/frontend/client/src/autotest/afe/JobListView.java
@@ -113,6 +113,7 @@
     
     @Override
     public void initialize() {
+        super.initialize();
         jobTable = new JobTable();
         jobTable.setRowsPerPage(JOBS_PER_PAGE);
         jobTable.setClickable(true);
diff --git a/frontend/client/src/autotest/afe/UserPreferencesView.java b/frontend/client/src/autotest/afe/UserPreferencesView.java
index 05529ea..d97f629 100644
--- a/frontend/client/src/autotest/afe/UserPreferencesView.java
+++ b/frontend/client/src/autotest/afe/UserPreferencesView.java
@@ -50,6 +50,7 @@
 
     @Override
     public void initialize() {
+        super.initialize();
         Panel container = new VerticalPanel();
         AfeUtils.populateRadioChooser(rebootBefore, "reboot_before");
         AfeUtils.populateRadioChooser(rebootAfter, "reboot_after");
diff --git a/frontend/client/src/autotest/common/JsonRpcProxy.java b/frontend/client/src/autotest/common/JsonRpcProxy.java
index eff1d58..7a1edc4 100644
--- a/frontend/client/src/autotest/common/JsonRpcProxy.java
+++ b/frontend/client/src/autotest/common/JsonRpcProxy.java
@@ -17,6 +17,7 @@
     public static final String AFE_BASE_URL = "/afe/server/";
     public static final String TKO_BASE_URL = "/new_tko/server/";
     private static final String RPC_URL_SUFFIX = "rpc/";
+    private static final String JSON_RPC_URL_SUFFIX = "jsonp_rpc/";
 
     private static String defaultBaseUrl;
     private static final Map<String, JsonRpcProxy> instanceMap =
@@ -26,10 +27,17 @@
     public static void setDefaultBaseUrl(String baseUrl) {
         defaultBaseUrl = baseUrl;
     }
+    
+    public static JsonRpcProxy createProxy(String baseUrl, boolean isPaddedJson) {
+        if (isPaddedJson) {
+            return new PaddedJsonRpcProxy(baseUrl + JSON_RPC_URL_SUFFIX);
+        }
+        return new XhrJsonRpcProxy(baseUrl + RPC_URL_SUFFIX);
+    }
 
     public static JsonRpcProxy getProxy(String baseUrl) {
         if (!instanceMap.containsKey(baseUrl)) {
-            instanceMap.put(baseUrl, new XhrJsonRpcProxy(baseUrl + RPC_URL_SUFFIX));
+            instanceMap.put(baseUrl, createProxy(baseUrl, false));
         }
         return instanceMap.get(baseUrl);
     }
@@ -38,6 +46,10 @@
         assert defaultBaseUrl != null;
         return getProxy(defaultBaseUrl);
     }
+    
+    public static void setProxy(String baseUrl, JsonRpcProxy proxy) {
+        instanceMap.put(baseUrl, proxy);
+    }
 
     /**
      * Make an RPC call.
diff --git a/frontend/client/src/autotest/common/PaddedJsonRpcProxy.java b/frontend/client/src/autotest/common/PaddedJsonRpcProxy.java
index fc742eb..e9f7060 100644
--- a/frontend/client/src/autotest/common/PaddedJsonRpcProxy.java
+++ b/frontend/client/src/autotest/common/PaddedJsonRpcProxy.java
@@ -48,7 +48,6 @@
             timeoutTimer = new Timer() {
                 @Override
                 public void run() {
-                    GWT.log("timeout firing " + requestId, null);
                     timedOut = true;
                     cleanup();
                     notify.showError("Request timed out");
@@ -68,7 +67,6 @@
             scriptTag = addScript(getFullUrl(rpcUrl), requestId);
             timeoutTimer.schedule(REQUEST_TIMEOUT_MILLIS);
             notify.setLoading(true);
-            GWT.log("request sent " + requestId + " <" + requestData + ">", null);
         }
 
         public void cleanup() {
@@ -101,19 +99,13 @@
         }
 
         public void handleResponseImpl(JavaScriptObject responseJso) {
-            GWT.log("response arrived " + requestId, null);
             cleanup();
             if (timedOut) {
-                GWT.log("already timed out " + requestId, null);
                 return;
             }
 
             JSONObject responseObject = new JSONObject(responseJso);
-            GWT.log("handling response " + requestId 
-                    + " (" + responseJso.toString().length() + ")", 
-                    null);
             handleResponseObject(responseObject, rpcCallback);
-            GWT.log("done " + requestId, null);
         }
     }
 
diff --git a/frontend/client/src/autotest/common/ui/CustomTabPanel.java b/frontend/client/src/autotest/common/ui/CustomTabPanel.java
index cb62f39..0034aec 100644
--- a/frontend/client/src/autotest/common/ui/CustomTabPanel.java
+++ b/frontend/client/src/autotest/common/ui/CustomTabPanel.java
@@ -84,8 +84,9 @@
     }
     
     public void addTabView(TabView tabView) {
+        tabView.attachToDocument();
         tabViews.add(tabView);
-        tabPanel.add(tabView, tabView.getTitle());
+        tabPanel.add(tabView.getWidget(), tabView.getTitle());
     }
     
     public List<TabView> getTabViews() {
diff --git a/frontend/client/src/autotest/common/ui/DetailView.java b/frontend/client/src/autotest/common/ui/DetailView.java
index 7ce7bd0..a4dca9f 100644
--- a/frontend/client/src/autotest/common/ui/DetailView.java
+++ b/frontend/client/src/autotest/common/ui/DetailView.java
@@ -34,6 +34,7 @@
     
     @Override
     public void initialize() {
+        super.initialize();
         resetPage();
         
         RootPanel.get(getFetchControlsElementId()).add(idInput);
diff --git a/frontend/client/src/autotest/common/ui/TabView.java b/frontend/client/src/autotest/common/ui/TabView.java
index 2d7f15a..08e4165 100644
--- a/frontend/client/src/autotest/common/ui/TabView.java
+++ b/frontend/client/src/autotest/common/ui/TabView.java
@@ -7,7 +7,7 @@
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.History;
 import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
 
 import java.util.Map;
 
@@ -19,18 +19,25 @@
  * tab from the "title" attribute of the HTML element.  This class also supports
  * lazy initialization of the tab by waiting until the tab is first displayed.
  */
-public abstract class TabView extends Composite {
-    protected boolean initialized = false;
-    protected String title;
+public abstract class TabView {
+    private boolean initialized = false;
+    private ElementWidget tabElement;
+    private String title;
     protected boolean visible;
     private Map<String, String> savedState;
-    
-    public TabView() {
-        ElementWidget thisTab = new ElementWidget(getElementId());
-        initWidget(thisTab);
-        title = thisTab.getElement().getAttribute("title");
+
+    public Widget getWidget() {
+        return tabElement;
     }
-    
+
+    public void attachToDocument() {
+        tabElement = new ElementWidget(getElementId());
+        title = tabElement.getElement().getAttribute("title");
+    }
+
+    // for subclasses to override
+    public void initialize() {}
+
     public void ensureInitialized() {
         if (!initialized) {
             initialize();
@@ -38,7 +45,7 @@
         }
     }
     
-    // primarily for subclasses to override
+    // for subclasses to override
     public void refresh() {}
     
     public void display() {
@@ -54,8 +61,7 @@
     protected boolean isTabVisible() {
         return visible;
     }
-    
-    @Override
+
     public String getTitle() {
         return title;
     }
@@ -80,8 +86,7 @@
      * @param arguments the parsed history arguments to use
      */
     public void handleHistoryArguments(Map<String, String> arguments) {}
-    
-    public abstract void initialize();
+
     public abstract String getElementId();
 
     protected void saveHistoryState() {
diff --git a/frontend/client/src/autotest/public/EmbeddedTkoClientTest.html b/frontend/client/src/autotest/public/EmbeddedTkoClientTest.html
new file mode 100644
index 0000000..441022c
--- /dev/null
+++ b/frontend/client/src/autotest/public/EmbeddedTkoClientTest.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+  <title>Embedded TKO Test</title>
+  <script type="text/javascript" language='javascript' 
+    src='autotest.EmbeddedTkoClient.nocache.js'>
+  </script>
+
+  <script type="text/javascript">
+    function initialize() {
+      Autotest.initialize("http://localhost");
+      var plot = Autotest.createMetricsPlot(document.getElementById("plot_canvas"));
+      queries = {}
+      queries["__main__"] = "SELECT test_name, AVG(IF(kernel LIKE '2.6.18%', iteration_value, NULL)) '2.6.18', STDDEV(IF(kernel LIKE '2.6.18%', iteration_value, NULL)) 'errors-2.6.18' FROM perf_view_2 WHERE test_name IN ('dbench', 'tbench') AND iteration_key = 'throughput' GROUP BY test_name";
+      queries["__2.6.18__"] = "SELECT test_idx, iteration_value FROM perf_view_2 WHERE test_name IN ('dbench', 'tbench') AND iteration_key = 'throughput' AND kernel LIKE '2.6.18%%' AND test_name = %s ORDER BY iteration_value";
+      plot.refresh({
+          plot : "Bar",
+          invert : [],
+          queries : queries
+      });
+    }
+  </script>
+</head>
+
+<body onload="initialize()">
+  Top text
+  <div id="plot_canvas"></div>
+  Bottom text
+</body>
diff --git a/frontend/client/src/autotest/tko/EmbeddedTkoClient.java b/frontend/client/src/autotest/tko/EmbeddedTkoClient.java
new file mode 100644
index 0000000..9aec733
--- /dev/null
+++ b/frontend/client/src/autotest/tko/EmbeddedTkoClient.java
@@ -0,0 +1,90 @@
+package autotest.tko;
+
+import autotest.common.JsonRpcProxy;
+import autotest.common.CustomHistory.HistoryToken;
+import autotest.tko.TableView.TableSwitchListener;
+import autotest.tko.TableView.TableViewConfig;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.Window;
+
+public class EmbeddedTkoClient implements EntryPoint, TableSwitchListener {
+    private String autotestServerUrl; 
+    private TestDetailView testDetailView; // we'll use this to generate history tokens
+
+    public void onModuleLoad() {
+        JsonRpcProxy.setDefaultBaseUrl(JsonRpcProxy.TKO_BASE_URL);
+        testDetailView = new TestDetailView();
+        injectNativeMethods();
+    }
+
+    /**
+     * Creates a global object named Autotest with the following methods.  This objects acts as the
+     * entry point for externally-written native code to create embeddable widgets.
+     * * initialize(autoservServerUrl) -- must call this before anything else, passing in the URL
+     *   to the Autotest server (i.e. "http://myhost").
+     * * createMetricsPlot(parent) -- returns a metrics plot object attached to the given parent 
+     *   element.
+     */
+    private native void injectNativeMethods() /*-{
+        var instance = this;
+        $wnd.Autotest = {
+            initialize: function(autotestServerUrl) {
+                [email protected]::initialize(Ljava/lang/String;)(autotestServerUrl);
+            },
+
+            createMetricsPlot: function(parent) {
+                return [email protected]::createMetricsPlot(Lcom/google/gwt/dom/client/Element;)(parent);
+            }
+        }
+    }-*/;
+
+    @SuppressWarnings("unused") // called from native
+    private void initialize(String autotestServerUrl) {
+        this.autotestServerUrl = autotestServerUrl;
+        JsonRpcProxy proxy = JsonRpcProxy.createProxy(autotestServerUrl + JsonRpcProxy.TKO_BASE_URL,
+                                                      true);
+        JsonRpcProxy.setProxy(JsonRpcProxy.TKO_BASE_URL, proxy);
+    }
+
+    @SuppressWarnings("unused") // called from native
+    private JavaScriptObject createMetricsPlot(Element parent) {
+        UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();
+        if (handler == null) {
+            return doCreateMetricsPlot(parent);
+        }
+
+        try {
+            return doCreateMetricsPlot(parent);
+        } catch (Throwable throwable) {
+            handler.onUncaughtException(throwable);
+            return null;
+        }
+    }
+
+    private JavaScriptObject doCreateMetricsPlot(Element parent) {
+        Plot plot = new MetricsPlot();
+        plot.setDrilldownTrigger();
+        plot.setListener(this);
+        parent.appendChild(plot.getElement());
+        return plot.getNativeProxy();
+    }
+
+    public HistoryToken getSelectTestHistoryToken(int testId) {
+        testDetailView.updateObjectId(Integer.toString(testId));
+        return testDetailView.getHistoryArguments();
+    }
+
+    public void onSelectTest(int testId) {
+        String fullUrl = autotestServerUrl + "/new_tko/#" + getSelectTestHistoryToken(testId);
+        Window.open(fullUrl, "_blank", "");
+    }
+
+    public void onSwitchToTable(TableViewConfig config) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/frontend/client/src/autotest/tko/GraphingView.java b/frontend/client/src/autotest/tko/GraphingView.java
index d537454..2afa330 100644
--- a/frontend/client/src/autotest/tko/GraphingView.java
+++ b/frontend/client/src/autotest/tko/GraphingView.java
@@ -33,6 +33,7 @@
     
     @Override
     public void initialize() {
+        super.initialize();
         frontendSelection.addItem("Metrics Plot", metricsPlotFrontend.getFrontendId());
         frontendSelection.addItem("Machine Qualification Histogram", 
                                   machineQualHistogramFrontend.getFrontendId());
diff --git a/frontend/client/src/autotest/tko/Plot.java b/frontend/client/src/autotest/tko/Plot.java
index ea86c91..1849335 100644
--- a/frontend/client/src/autotest/tko/Plot.java
+++ b/frontend/client/src/autotest/tko/Plot.java
@@ -46,11 +46,28 @@
         }
     }-*/;
 
+    /**
+     * Get a native JS object that acts as a proxy to this object.  Currently the only exposed
+     * method is refresh(params), where params is a JS object.  This is only necessary for allowing
+     * externally-written native code to use this object without having to write out the full JSNI
+     * method call syntax.
+     */
+    public native JavaScriptObject getNativeProxy() /*-{
+        var instance = this;
+        return {
+            refresh: function(params) {
+                jsonObjectParams = @com.google.gwt.json.client.JSONObject::new(Lcom/google/gwt/core/client/JavaScriptObject;)(params);
+                [email protected]::refresh(Lcom/google/gwt/json/client/JSONObject;)(jsonObjectParams);
+            }
+        };
+    }-*/;
+
     @SuppressWarnings("unused") // called from native code (see setDrilldownTrigger)
     private void showDrilldown(JavaScriptObject drilldownParamsJso) {
         UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();
         if (handler == null) {
             showDrilldownImpl(new JSONObject(drilldownParamsJso));
+            return;
         }
 
         try {
diff --git a/frontend/client/src/autotest/tko/SpreadsheetView.java b/frontend/client/src/autotest/tko/SpreadsheetView.java
index d6e315b..9c3fdb1 100644
--- a/frontend/client/src/autotest/tko/SpreadsheetView.java
+++ b/frontend/client/src/autotest/tko/SpreadsheetView.java
@@ -89,6 +89,7 @@
 
     @Override
     public void initialize() {
+        super.initialize();
         normalDataSource.setSkipNumResults(true);
         latestDataSource.setSkipNumResults(true);
         
diff --git a/frontend/client/src/autotest/tko/TableView.java b/frontend/client/src/autotest/tko/TableView.java
index b96b241..8553015 100644
--- a/frontend/client/src/autotest/tko/TableView.java
+++ b/frontend/client/src/autotest/tko/TableView.java
@@ -105,6 +105,7 @@
 
     @Override
     public void initialize() {
+        super.initialize();
         for (FieldInfo fieldInfo : TkoUtils.getFieldList("all_fields")) {
             namesToFields.put(fieldInfo.name, fieldInfo.field);
             fieldsToNames.put(fieldInfo.field, fieldInfo.name);