Zero 通過集成 Groovy 腳本編制簡化創建過程
開始之前
本文假設您已經下載了 Project Zero M4,並使用它創建過一個或多個應用程序。您需要對 Groovy 腳本編制、Ajax 技術和 HTML 有基本的理解,這些內容可以通過 Zero 的教程和示例獲得。
簡介:Google Charts API
Google Charts 是一個非常出色的服務,它讓開發人員可以使用簡單的 HTTP GET 請求來生成圖形和圖表。客戶機發送請求到 http://chart.apis.google.com/chart, 同時發送的還有一個或多個查詢參數,表明需要的圖表的類型;這些查詢參數的完整列表可在 Google Charts API 文檔中找到。目前,該 API 允許對很多屬性進行控制, 包括圖表的標題、布局、顏色、軸線、數據和說明。圖 1 是使用該 API 的一個簡單的例子。
圖 1. 示例圖表:(來源:Google.com)
如果在圖像上單擊右鍵並選擇 Properties,就可以看到用於生成該圖表的 URL。在後面的小節中我將談到查詢字符串參數的具體作用。但是,查詢參數的名稱相當老套 ,在將它們組合到一個 URL 時,得到的 URL 冗長繁瑣。在下一節中,您將開始使用 Project Zero 上運行的 Groovy 代碼來封裝這些復雜的 URL。
公開更加 RESTful 的圖表 API
能夠在浏覽器中僅僅通過地址欄創建圖表當然很好,但是正如 簡介 小節中所述,這樣做實際上是很繁瑣的。能夠在地址欄中創建圖表,對於非開發人員來說這看上去很 有用,但是一旦稍微復雜一些,URL 就會顯得太長。更好的設計方法是使用 HTTP POST 和 JSON(用於開發人員)公開圖表的創建,並在此基礎上提供圖形化的界面(用於 非開發人員)。這將使這兩種用戶都更容易地調試圖表布局或數據方面的問題。可以使用 Project Zero 實現這種方法,具體做法是將圖表 URL 的創建封裝在一個 Groovy 腳本中,然後創建一個基於 Ajax 的 Web 界面,該界面通過 HTTP 調用那個腳本。
創建示例項目
為了展示您的工作,您需要創建兩個項目:一個存放 Groovy 包裝器代碼,另一個用於存放示例 Ajax UI。您應該創建兩個 Zero 應用程序 — 一個名為 zero.charts, 另一個名為 chart.maker — 以分別存放 Groovy 代碼和 Ajax 代碼。這兩個項目是分離的,以便允許不同的應用程序重用 Groovy 代碼。您將從 zero.charts 應用程序中 開始。
在 Groovy 中生成圖表 URL
使用 Groovy 簡化圖表創建的第一步是定義用於指定圖表細節的數據結構。您不會使用 Google Charts 所用的原始參數名稱,而是用 JSON 定義一個更詳細的數據結構 ;使用 JSON 意味著可以依賴標准數據類型(例如實際的整數數組,而不是包含以逗號分隔的串行化的整數字符串),並且更容易看到什麼值被傳遞給圖表生成器。清單 1 展示了 Ajax 客戶機與 Groovy 腳本之間的通信將使用的數據結構。
清單 1. 使用 JSON 的示例圖表定義
{
title: "Sales for 1H 2008",
type: "bvg",
height: 300,
width: 300,
data: [34, 21, 28, 19, 48, 40],
xaxis: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
yaxis: [0, 40]
}
在開發期間,可以使用 FireBug 或簡單的 JavaScript 警告等工具檢查清單 1 中所示的 JSON 對象。與 http://chart.apis.google.com/chart? cht=bvg&chs=500x600&chtt=Sales%20for%201Q%202008&chd=t:134,219,188&chxt=x,y&chxl=0:|Jan|Feb|Mar|1:||300 這樣冗長的字符串相比,這樣 的結構更容易調試和驗證。
當然,歸根到底,您仍然需要發送這樣一個 URL 到 Google 的服務器,以獲得所需的圖表。為此,您將編寫一個簡單的 Groovy 方法,該方法將 JSON 對象中的字段值 復制到一個圖表 URL 中。借助 Groovy 的 GString 類型,您可以使 URL 字符串只有一行,而不必創建一個 java.lang.StringBuilder 並逐個添加值。這個 Groovy 方法 的代碼如清單 2 所示。
清單 2. 構造圖表 URL 的 Groovy 代碼
def create(chart)
{
return "http://chart.apis.google.com/chart?chxt=x,y" +
"&cht=${chart.type}&chs=${chart.width}x${chart.height}" +
"&chtt=${chart.title} &chd=t:${chart.data.join(',')}" +
"&chxl=0:|${chart.xaxis.join('|')}|1:|${chart.yaxis.join('|')}";
}
基本上,清單 2 中的代碼直接從 JSON 對象獲得值並(使用 $ 語法)將這些值嵌入到 URL 中。惟一不同的是當 JSON 字段為數組時的情況,在此情況下, 必須將它們的值連接成一個字符串。使用 join() 方法很容易做到這一點。
您應該將清單 2 中的代碼復制到一個名為 /zero.charts/app/scripts/charts.groovy 的文件中。通過將該代碼放到 /app/scripts 目錄中,可以讓應用程序中的其他 Groovy 腳本以及在依賴關系列表中包括 zero.charts 的任何應用程序訪問到它。使該代碼 易於重用的最後一步是為之創建一個 Groovy 綁定,這將使開發人員可以直接在代碼中調用 create() 方法,而不必使用 Zero 的更通用的 invokeMethod() 。要添加 Groovy 綁定,將清單 3 中的 Java™ 類定義復制到 /zero.charts/java 目錄中。如果之前沒有提供這個綁定,那麼開發人員必須 輸入粗體顯示的代碼行。
清單 3. charts.groovy 的 Groovy 綁定
package zero.charts;
import java.io.FileNotFoundException;
import java.util.Map;
import org.codehaus.groovy.runtime.MethodClosure;
import zero.core.groovysupport.bindings.InvokeBindings;
public class ChartsBindings extends InvokeBindings
{
public void addVariables(Map<String, Object> variables)
{
super.addVariables(variables);
variables.put("create", new MethodClosure(this, "create"));
}
public Object create(Map<String, Object> chart)
throws FileNotFoundException, NoSuchMethodException
{
return invokeMethod("charts.groovy", "create", new Object[]{ chart });
}
}
在繼續之前,注意傳遞給清單 3 中 create() 的參數類型為 java.util.Map。在 Zero 中,JSON 對象和 Maps 是一樣的。Zero 的所有 I/O API 都知道如何在適當的時 候完成 Map 與 JSON 之間的串行化,所以您不必學習專門的 API 就可以直接操縱 JSON 對象。
一旦清單 3 中的 Java 類就緒,就必須將其注冊到配置文件中,以便讓 Zero 知道它的存在。清單 4 中的文本必須添加到 /zero.charts/config/zero.config 中,以 確保 Groovy 綁定有效,並且對 create() 的調用能夠得到適當的處理。
清單 4. Groovy 綁定的配置
/config/bindings/.groovy += ["zero.charts.ChartsBindings"]
通過 HTTP POST 請求圖表
現在,您有了 URL 構造代碼,接下來只需使用一個 RESTful API 來公開它。為此,您將創建一個名為 charts 的 RESTful 資源類型,並使用 HTTP POST 方法接受圖表 規范(使用 JSON)並返回圖表 URL。這個行為與 RESTful 應用程序中通常實現的行為稍微有些不同。通常,HTTP POST 會導致代碼的執行,該代碼在與請求 URI 相同的域 中創建一個資源,並返回那個新資源的 URI。您將通過構造一個指向 Google Charts 服務的 URI 來 “創建” 一個資源,當客戶機訪問那個 URI 時,將得到圖表。因此, 您的服務真正創建並返回的惟一的內容就是一個圖表 URI,該 URI 位於 HTTP 響應的 Location 報頭中。
清單 5 展示了 charts 資源的 Groovy 代碼。它的惟一的 HTTP 方法是 POST,該方法是在 onCreate() 方法中實現的。您應該將該代碼復制到一個名為 /zero.charts/app/resources/charts.groovy 的文件中。
清單 5. 用於圖表創建的 REST API
import zero.json.Json;
/**
*
* @success 201 Returns the URI for the desired chart in the Location header.
* @error 500 The chart definition in the request body was not valid JSON.
* @format application/json
*
*/
def onCreate()
{
def chart = Json.decode(request.input[]);
def chartImageURL = create(chart);
request.headers.out.Location = chartImageURL;
request.status = 201;
}
在談論用戶界面之前,我們花點時間使用 RESTdoc 測試界面對 REST API 進行測試。啟動 zero.charts 應用程序(zero run),並在 Web 浏覽器中訪問 http://localhost:8080/resources/docs/charts。您將看到,charts 資源有一個方法(POST),如果單擊它,將看到一個測試表單(如圖 2 所示)。將 JSON 數據從 清 單 1 復制到請求體中,並單擊 Send。您看到的響應應該包括一個 Location 頭,其中有一個 Google Charts URI。
圖 2. 用於圖表創建的 RESTdoc 測試表單
REST API 就緒後,現在可以在應用程序中使用它。下一節將展示如何使用 Ajax 創建可視化的圖表構建器。
設計更有用的界面
REST API 可以讓開發人員更易於生成圖表,它並不是用來幫助 99% 的不用編寫代碼的計算機用戶的,而是針對那些偶爾為他們的文檔和演示創建圖表的用戶。對於這些 用戶,可以使用一個 HTML 表單、一點 JavaScript 和最簡單的 Ajax API XMLHttpRequest,創建一個簡單的基於 Web 的圖表構建器。圖 3 展示了最終結果:Web 頁面的 一側允許用戶輸入圖表描述,而另一側則顯示生成的圖表。
圖 3. 用於生成圖表的 Web 界面
要開始創建該用戶界面,首先必須創建 chart.maker 應用程序與 zero.charts 應用程序之間的一個依賴關系。可以通過將 zero:zero.charts 添加到 /chart.maker/config/ivy.xml 實現。接下來的兩個小節將展示最重要的 markup 部分以及界面所需的代碼。通過下載本文包含的 示例項目,並查看 chart.maker 項目, 可以看到完整的 markup 和代碼。
將用戶輸入映射為 API 輸入
如圖 3 所示,用於獲取用戶圖表的表單非常簡單。除了一個輸入值外,該表單上的所有東西都可以很輕松地轉換為所需的 JSON 格式。惟一需要額外步驟的輸入是圖表 類型,它是由 Google Charts API 使用兩個或三個字母的縮寫表示的。該表單顯示對圖表類型(Line Graph、Bar Chart - Vertical 和 Bar Chart - Horizontal)的完整 描述,因此 HTML 下拉菜單需要在這些描述與縮寫(分別為 lc、bvg 和 bhg)之間映射。清單 6 展示了所需 HTML 下拉菜單的定義。您可以看到用粗體表示的圖表類型代 碼。所有其他表單輸入都是簡單的文本框(<input type="text"/>)
清單 6. 用於圖表類型的 HTML 下拉菜單
<select id="chartType">
<option value="lc">Line Graph</option>
<option value="bvg">Bar Graph - Vertical</option>
<option value="bhg">Bar Graph - Horizontal</option>
</select>
除了下拉菜單 markup 外,對於那些不熟悉這種控件的用戶,還提供了從菜單中獲取所選值的代碼。與簡單的文本框不同,不能直接讀取該控件的 value 屬性 — 必須 遍歷下拉菜單的選項,直到發現當前選中的項(selected)。清單 7 包含了實現這一功能所需的代碼。
清單 7. 發現選中的圖表類型的 JavaScript
function getSelection(elementName)
{
var select = document.getElementById(elementName);
for (var i = 0; i < select.options.length; i++)
if (select.options[i].selected)
return select.options[i].value;
return null;
}
另一個重要的 UI 元素是表單底部的 Create It! 按鈕。這個按鈕並非用於提交表單,而是鏈接到一個 JavaScript 函數,該函數發出一個對 REST API 的 Ajax 請求。 為此,需要將一個 JavaScript 函數調用添加到該按鈕的 onClick 事件中。清單 8 展示了鏈接到 createChart() 函數的按鈕單擊事件,您將在下一節中實現該函數。
清單 8. 使用 JavaScript 初始化圖表創建
<input type="button" value="Create It!" onClick="createChart();"/>
最後要詳細談到的 UI 元素就是顯示所生成圖表的圖像。清單 9 展示了如何編寫一個當頁面裝載時會隱藏的 HTML <img/> 標記。您將使用 JavaScript 將該標記 的源設為所生成圖表的 URL,然後使之可見。
清單 9. 用於顯示圖表的 HTML 圖像
<img id="chartImage" style="display:none;" src=""/>
您應該查看 chart.maker 示例項目中的 /public/index.html 文件,看看所有這些 markup 和代碼。接下來的小節將談到面向 Ajax 的 JavaScript(也被包括在該文件 中),它用於操縱 UI 元素,以顯示反映用戶輸入的圖表。
使用 Ajax 顯示生成的圖表
與 REST API 的交互就是實現 createCharts() 函數,使之讀取所有表單輸入值,將它們打包到一個 JSON 對象中,並將 JSON 對象作為 HTTP POST 請求的一部分發送 到 charts 資源。HTTP POST 請求完成後,您將從 Location 響應報頭讀取圖表 URL,並設置(重置)圖表圖像的源。清單 10 顯示了實現這一點的代碼。
清單 10. 使用 Ajax 創建和顯示圖表
function getHttpClient()
{
var hasXHR = window.XMLHttpRequest;
return hasXHR ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
}
function createChart()
{
// step 1
var chart = {
title: getValue("chartTitle"),
type: getSelection("chartType"),
height: getValue("chartHeight"),
width: getValue("chartWidth"),
data: getValues("chartData"),
xaxis: getValues("xaxis"),
yaxis: getValues("yaxis")
};
// step 2
var http = getHttpClient();
http.open("POST", "/resources/charts", true);
// step 3
http.onreadystatechange = function() {
if (http.readyState != 4)
return;
var chartImage = document.getElementById("chartImage");
chartImage.src = http.getResponseHeader("Location");
chartImage.style.display = "block";
}
// step 4
http.send(JSON.stringify(chart));
}
我們來逐步講解這段代碼:
在 createCharts() 中,首先做的事情是使用適當的名稱-值對創建一個 JSON 對象。getValue(s) 方法是類似於 清單 7 中的 getSelection() 方法的簡單實用程序。 它們的實現非常普通,可以從示例項目中查看它們。這裡要特別注意的是,字段名稱與 /app/scripts/charts.groovy 文件期望的名稱相匹配,並且值使用適當的格式。
第二步是創建一個 XMLHttpRequest 對象,並發起一個對 /resources/charts 的 HTTP POST 請求。getHttpClient() 方法隱藏有一些條件,以確保為適當的 Web 浏覽 器實例化適當的類。
第三步是定義發送請求和收到響應之後的動作。在這個例子中,您將獲取 Location 報頭的值,並使用它設置 chartImage.src 的值;然後,修改 chartImage 樣式,使 之不再對用戶隱藏。
最後一步是發送 HTTP POST 請求。這包括將 JSON 對象轉換成可放入 HTTP POST 請求體中的字符串。由於沒有標准的 JSON-to-string API,所以使用來自 http://json.org 的開源庫。createChart() 最後的 JSON.stringify() API 調用是在一個名為 json2.js 的文件中定義的,該文件可以從 http://www.json.org/json2.js 獲得。您應該從 http://json.org 下載這個 JavaScript 庫,並將它添加到 /chart.maker/public 目錄。一旦這個庫准備就緒,createCharts() 的最後一行將順利運行。
結束語
您看到了如何創建精巧的 mashup 應用程序,並且在此過程中使 Google Charts API 更易於在 Zero 應用程序之間重用。示例 Ajax 界面只涉及可視化圖表構建器中的 一些基本特性,但是服務器端代碼可以支持您想要添加的任何其他特性。在將 RESTful API 用於寶貴的服務和數據方面,Zero 對 Groovy 腳本編制的集成再次被證明可以 節省大量的時間。
本文配套源碼